Google gRPC(Google Remote Procedure Call)是一个高性能、开源的远程过程调用框架,它允许客户端直接调用远程服务器上的方法,就像调用本地方法一样,屏蔽了网络通信的复杂性。
1. 配置安装
在正式编写代码之前,我们需要先完成开发环境的搭建与依赖包的安装。执行下面代码创建一个单独的虚拟环境,并安装 Python GRPC 需要的包。
# 创建虚拟环境 conda create -n grpc-env python=3.10 # 安装 gRPC 相关包 pip install grpcio grpcio-tools
- grpcio 用于运行 gRPC 客户端和服务端
- grpcio-tools 用于编译 .proto 文件
安装之后,我们创建一个 demo.proto 的 gRPC 服务定义文件,内容如下:
syntax = "proto3"; service Greeter { rpc sayHello (Request) returns (Response) {} } message Request { string name = 1; int32 age = 2; } message Response { string message = 1; }
- service Greeter:定义了一个服务,包含一个
sayHello
方法
- Request/Response:定义了请求和响应的数据结构
接下来,在命令行中输入以下命令编译 proto 文件:
python -m grpc_tools.protoc -I. --python_out=pyi_out:. --grpc_python_out=. demo.proto
此时,会根据 proto 文件生成两个 py 文件:
- demo_pb2.py 对 message 进行操作的封装
- demo_pb2.pyi 用于提供静态类型信息提示
- demo_pb2_grpc.py 对 service 进行操作的封装
2. 远程调用
我们将接下来分别编写服务端与客户端的代码,实现一个完整的 gRPC 通信流程。
2.1 服务端
import grpc from demo_pb2_grpc import GreeterServicer from demo_pb2_grpc import add_GreeterServicer_to_server from demo_pb2 import Request, Response from concurrent import futures # 实现服务接口 class MyGreeterServicer(GreeterServicer): # request 表示客户端的请求参数对象 # context 表示一次客户端请求的上下文信息 def sayHello(self, request : Request, context : grpc.ServicerContext): print('客户端:', context.peer()) for key, value in context.invocation_metadata(): print('元信息:', key, value) print('请求参数:', request.name, request.age) print('-' * 50) # 当执行这一行代码时,终止后续调用并返回错误 context.abort(code=grpc.StatusCode.NOT_FOUND, details='出错了') return Response(info='ok') def run_server(): # 创建服务对象 # thread_pool: 用于执行 RPC 调用的线程池。服务端接收到请求后,会从线程池中分配线程来处理 handler 中的逻辑。 server = grpc.server(thread_pool=futures.ThreadPoolExecutor(max_workers=10)) # 设置服务端口 server.add_insecure_port('0.0.0.0:50052') # 注册服务接口 add_GreeterServicer_to_server(MyGreeterServicer(), server) # 开启服务 server.start() server.wait_for_termination() if __name__ == '__main__': run_server()
2.2 客户端
import grpc from demo_pb2 import Request, Response from demo_pb2_grpc import GreeterStub def demo(): channel = grpc.insecure_channel('127.0.0.1:50052') stub = GreeterStub(channel) # 构造请求元信息 metadata = (('token', 'abc123'), ('trace_id', 'trace-xyz-789')) # 构造请求参数信息 request = Request(name='obama', age=20) response : Response = stub.sayHello(request, metadata=metadata) print('远程调用结果:', response.info) if __name__ == '__main__': demo()