python grpc简单使用
1、rpc和grpc关系
RPC( Remote Procedure Call Protocol),直译来看就是远程过程调用协议。
它提供了一套机制,使得应用程序之间可以进行通信,使用时客户端调用server端的接口就像调用本地的函数一样方便。并且server端和client端不限语言,任何语言遵循protobuf协议都可以进行通信。
如下图所示就是一个典型的RPC结构图。
而gRPC是一个开源的RPC框架,建立在HTTP2的基础设施之上,因此具备了HTTP2协议的一系列优势。
- 二进制分帧的数据传输
- 多路复用
- 服务端推送
- 头部压缩
2、为什么要使用gRPC
有的人可能疑惑,传统的restful API也可以体提供服务接口,同样可以实现接口调用;为什么要用gRPC呢?
是的,gRPC和restful API都提供了一套通信机制,用于server/client模型通信,而且它们都使用http作为底层的传输协议(严格来说gRPC使用的是HTTP2,而restful API不一定)。但是gRPC在以下场景下有着天然优势:
- 需要对接口进行严格约束的情况,比如我们提供了一个公共的服务,很多人,甚至公司外部的人也可以访问这个服务,这时对于接口我们希望有更加严格的约束,我们不希望客户端给我们传递任意的数据,尤其是考虑到安全性的因素,我们通常需要对接口进行更加严格的约束。这时gRPC就可以通过protobuf来提供严格的接口约束。
- 对于性能有更高的要求时。有时我们的服务需要传递大量的数据,而又希望不影响我们的性能,这个时候也可以考虑gRPC服务,因为通过protobuf我们可以将数据压缩编码转化为二进制格式,通常传递的数据量要小得多,而且通过http2我们可以实现异步的请求,从而大大提高了通信效率。
3、python使用gRPC示例
3.1、通过protobuf定义接口和数据类型
// example.proto
syntax = "proto3"; // 指明使用proto3语法
option cc_generic_services = true;
service ExampleService {
rpc SendMessage(Request) returns (Response); //一个服务中可以定义多个接口,也就是多个函数功能
}
message Request {
string message = 1; //不是赋值,而是指定唯一编号
}
message Response {
string reply = 1;
}
3.2、安装相关模块
pip install grpcio
# 安装 python 下的 protoc 编译器
pip install grpcio-tools
3.3、使用 protoc 编译 proto 文件生成接口代码
python -m grpc_tools.protoc -I. proto/example.proto --pythoout=. --grpc_python_out=.
- -I 指定proto文件输入路径
- --pythoout= 编译生成处理 protobuf 相关的代码的路径, 这里生成到当前目录
- --grpc_python_out= 编译生成处理 grpc 相关的代码的路径, 这里生成到当前目录
说明: -I参数后的输入文件路径的上级目录就是 --pythoout 和 --grpc_python_out 的当前路径,也就是.
比如我当前目录结构是这样
如果想要生成的接口代码文件在proto_2文件目录下,怎执行命令
cd grpc_test
python -m grpc_tools.protoc --python_out=. -I. protos_2/test.proto --grpc_python_out=.
执行后:
3.4、编写服务端
# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @FileName: server.py
# @Time : 2024/4/28 18:03
# @Author : zcc
import grpc
from concurrent import futures
from protos import example_pb2
from protos import example_pb2_grpc
class ExampleServicer(example_pb2_grpc.ExampleServiceServicer):
def SendMessage(self, request, context):
return example_pb2.Response(reply=f"Received: {request.message},我是服务端!")
def run_server():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
example_pb2_grpc.add_ExampleServiceServicer_to_server(ExampleServicer(), server)
server.add_insecure_port('[::]:50052')
server.start()
server.wait_for_termination()
if __name__ == '__main__':
run_server()
3.5、编写客户端
# !/usr/bin/env python
# -*- coding: utf-8 -*-
# @FileName: client.py
# @Time : 2024/4/28 17:47
# @Author : zcc
import grpc
from protos import example_pb2
from protos import example_pb2_grpc
def run_client():
with grpc.insecure_channel('localhost:50052') as channel:
stub = example_pb2_grpc.ExampleServiceStub(channel)
response = stub.SendMessage(example_pb2.Request(message="Hello gRPC!"))
print(f"Response from server: {response.reply}")
if __name__ == '__main__':
run_client()
先运行服务端,后运行客户端,就可以看到二者之间的通信了。