了解gRPC框架


主要以 Python 作为基础进行演示和说明!

gRPC 是一个高性能、通用的开源 RPC 框架,基于 HTTP2 协议标准设计开发,默认采用 Protocol Buffers 数据序列化协议,支持多种开发语言。

了解gRPC框架


1. 什么 gRPC 框架

New to gRPC? Start with the following pages

RPC 框架的目标就是让远程服务调用更加简单、透明,其负责屏蔽底层的传输方式(TCP/UDP)、序列化方式(XML/Json)和通信细节。服务调用者可以像调用本地接口一样调用远程的服务提供者,而不需要关心底层通信细节和调用过程。

了解gRPC框架

常见 gRPC 的应用场景,主要侧重于后端服务之间进行调用,当然也可以使用于移动端。

  • gRPC 的功能优点

    • 高兼容性、高性能、使用简单
  • gRPC 的组成部分

    • 使用 http2 作为网络传输层
    • 使用 protobuf 这个高性能的数据包序列化协议
    • 通过 protoc gprc 插件生成易用的 sdk
  • gRPC 的通信方式

    • 客服端一次请求, 服务器一次应答
    • 客服端一次请求, 服务器多次应答(流式)
    • 客服端多次请求(流式), 服务器一次应答
    • 客服端多次请求(流式), 服务器多次应答(流式)

了解gRPC框架 - 什么gRPC框架


2. 使用 http2 协议

为什么会选用 http2 作为 grpc 的传输协议?

  • 除了速度之外,最大的原因就是最大程度的服务兼容性。因为 gRPC 基于 http2 协议,加之市面上主流的代理工具也都支持 http2 协议,所以自然就支持 gRPC 了。

了解gRPC框架 - 使用http2协议

  • 我们都知道 http 协议的几个重要版本,随着不断更新迭代而来,也解决了不同的问题。比如,在 http1.0 中最大的槽点就是短连接,随着 http1.1 的出世解决了该性能问题,并且增加了丰富的 header 语义。

了解gRPC框架 - 使用http2协议

  • 在前几年,网站基本都是使用的 http1.1 协议。但是,该协议也是存在着很多问题,比如队头阻塞,即一个连接只能跑一个请求,在这个请求没返回数据之前,其他请求就不能占用该连接。通常来说,我们使用的 chrome 浏览器默认会并发发起六个连接,如果请求太多的话,就需要等待了。虽然 http1.1 支持 pipeline,同时也有一些解决方案,但是还是不够优秀,所以就有了 http2 协议。

了解gRPC框架 - 使用http2协议

了解gRPC框架 - 使用http2协议

  • http2 协议最大的优点在于多路复用,这里的多路复用不是指传统类似 epolltcp 连接的复用,而是协议层的多路复用,把一个个的请求封装成 stream 流,这些流是可以并发交错请求,没有 http1.1 那种队头阻塞问题。

了解gRPC框架 - 使用http2协议

了解gRPC框架 - 使用http2协议

了解gRPC框架 - 使用http2协议

  • 当然,未来还会有更好的,http3 协议。

了解gRPC框架 - 使用http2协议


3. gRPC 的核心概念

An introduction to gRPC and protocol buffers.

  • ProtoBuf buffer 是一种数据表达方式,以 .proto 结尾的数据文件,我们可以将其类比为 jsonxml 等文件。针对 ProtoBuf buffer 数据源,可以利用 protoc 工具来生成各种语言的访问类。其优点在于,编解码速度更快且传输的数据更小。

了解gRPC框架 - gRPC的核心概念

了解gRPC框架 - gRPC的核心概念

了解gRPC框架 - gRPC的核心概念

了解gRPC框架 - gRPC的核心概念


  • [1] 编写 proto 文件
    • 编写 helloworld.proto 文件
// 指定使用协议语法
syntax = "proto3";

// 定义一个包
package greeterpb;

// 关键字server来定一个服务
// gRPC的服务是通过参数和返回类型来指定可以远程调用的方法
service Greeter {
    rpc SayHello(HelloRequest) returns (HelloReply) {}
    rpc SayHelloAgain(HelloRequest) returns (HelloReply) {}
}

// 定义消息请求
// 关键字message来定义请求或相应需要使用的消息格式
message HelloRequest {
    string name = 1;
}

// 定义消息响应
// 关键字message来定义请求或相应需要使用的消息格式
message HelloReply {
    string message = 1;
}
  • [2] 使用 protoc 编译
    • --python_out: 编译生成处理 protobuf 相关的代码的路径
    • --grpc_python_out: 编译生成处理 grpc 相关的代码的路径
    • -I: 指定 proto 的文件路径
# 编译proto文件
$ python -m grpc_tools.protoc \
    --python_out=. --grpc_python_out=. -I. \
    helloworld.proto

# 编译后生成的代码
helloworld_pb2.py
helloworld_pb2_grpc.py
  • [3] 添加 protobuf 运行时
    • 编写 helloworldgrpc 实现
# 服务器端
# helloworld_grpc_server.py

from concurrent import futures
import time
import grpc
import helloworld_pb2
import helloworld_pb2_grpc

# 实现proto文件中定义的GreeterServicer
class Greeter(helloworld_pb2_grpc.GreeterServicer):
    # 实现proto文件中定义的rpc调用
    def SayHello(self, request, context):
        return helloworld_pb2.HelloReply(message = 'hello {msg}'.format(msg = request.name))

    def SayHelloAgain(self, request, context):
        return helloworld_pb2.HelloReply(message='hello {msg}'.format(msg = request.name))

def serve():
    # 启动rpc服务
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
    server.add_insecure_port('[::]:50051')
    server.start()
    try:
        while True:
            time.sleep(60*60*24)
    except KeyboardInterrupt:
        server.stop(0)

if __name__ == '__main__':
    serve()
# 客户端
# helloworld_grpc_client.py

import grpc
import helloworld_pb2
import helloworld_pb2_grpc

def run():
    # 连接rpc服务器
    channel = grpc.insecure_channel('localhost:50051')
    # 调用rpc服务
    stub = helloworld_pb2_grpc.GreeterStub(channel)
    response = stub.SayHello(helloworld_pb2.HelloRequest(name='czl'))
    print("Greeter client received: " + response.message)
    response = stub.SayHelloAgain(helloworld_pb2.HelloRequest(name='daydaygo'))
    print("Greeter client received: " + response.message)

if __name__ == '__main__':
    run()
  • [4] 项目中集成
# 运行启动服务端
$ python3 helloworld_grpc_server.py

# 运行启动客户端
$ python3 helloworld_grpc_client.py

4. 快速使用 gRPC 框架

This guide gets you started with gRPC in Python with a simple working example.

  • [1] 构建基础环境
    • 创建虚拟环境
# need python3.5+
$ python -m pip install virtualenv
$ virtualenv venv
$ source venv/bin/activate
$ python -m pip install --upgrade pip
  • [2] 安装 gRPC 包
    • grpcio-tools 包含了生成对应的程序代码
# install gRPC
$ python -m pip install grpcio
$ python -m pip install grpcio-tools

# install it system wide
$ sudo python -m pip install grpcio
  • [3] 下载示例程序
    • 仓库非常大,不太好下载,需要梯子强壮
# clone the repository
$ git clone -b v1.37.1 https://github.com/grpc/grpc

# say "hello, world"
$ cd grpc/examples/python/helloworld
  • [4] 运行示例程序
    • 运行了一个客户机-服务器应用程序
# run the server
$ python greeter_server.py

# run the client
$ python greeter_client.py
  • [5] 更新 gRPC 服务
    • 即对现有的服务进行更新,来提供不一样的特性
# examples/protos/helloworld.proto
// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  // Sends another greeting
  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}
# 进入对应目录
cd examples/python/helloworld

# 重新生成代码
$ python -m grpc_tools.protoc -I../../protos \
    --python_out=. --grpc_python_out=. \
    ../../protos/helloworld.proto

# 生成文件 - 用来和protobuf进行数据交互
helloworld_pb2.py
# 生成文件 - 用来和grpc进行数据交互
helloworld_pb2_grpc.py
# 调整客户端代码 - greeter_client.py
def run():
  channel = grpc.insecure_channel('localhost:50051')
  stub = helloworld_pb2_grpc.GreeterStub(channel)
  response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
  print("Greeter client received: " + response.message)
  response = stub.SayHelloAgain(helloworld_pb2.HelloRequest(name='you'))
  print("Greeter client received: " + response.message)

# 调整服务端代码 - greeter_server.py
class Greeter(helloworld_pb2_grpc.GreeterServicer):
  def SayHello(self, request, context):
    return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)

  def SayHelloAgain(self, request, context):
    return helloworld_pb2.HelloReply(message='Hello again, %s!' % request.name)
...
# run the server
$ python greeter_server.py

# run the client
$ python greeter_client.py

5. gRPC 的应用场景

RPC 的使用场景:分布式系统

随着微服务的不断发展,基于语言中立性的原则构建微服务,逐渐成为一种主流设计模式。例如对于后端并发处理要求高的微服务,比较适合采用 Go 语言构建,而对于前端的 Web 界面,则更适合 JavaScript。因此,基于多语言的 gRPC 框架来构建微服务,是一种比较好的技术选择。

  • [1] gRPC Microservice

了解gRPC框架 - gRPC的应用场景

  • [2] gRPC K8S

了解gRPC框架 - gRPC的应用场景


6. 参考链接地址

送人玫瑰,手有余香!


文章作者: Escape
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Escape !
  目录