GRPC
安装:
pip install grpcio #gRPC 的安装
pip install protobuf #ProtoBuf 相关的 python 依赖库
pip install grpcio-tools #python grpc 的 protobuf 编译工具
protobuf
// camera.proto,定义数据格式
syntax = "proto3"; // 定义proto3版本,是推荐版本,也可以用proto2
service Camera { //定义服务
rpc CreateCamera(CameraInfo) returns (CameraInfo) {}
rpc RequestImage(CameraInfo) returns (ImageResponse) {}
}
//定义相关数据类型,
message NDArray {
string data_type = 1;
repeated int32 shape = 2;
bytes data = 3;
}
message CameraInfo { //定义数据类型,用于传参,可有嵌套使用,类似于类,只是赋值为编号
string camera_name = 1;
string camera_ip = 2;
float image_exposure = 3;
float point_cloud_exposure = 4;
int32 running_flag = 5;
repeated float roi = 6;//repeated用于数组定义
int32 gain = 7;
bool auto_gain = 8;
}
protobuf常见数据类型包括:
float、double、int32、int64、uint32、uint64、sint32、sint64、bool、string、bytes等
用protobuf生成python文件
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. helloworld.proto
# 生成两个.py文件
# camera_pb2.py: 用来和 protobuf 数据进行交互, 名字跟proto文件名有相关性
# camera_pb2_grpc.py: 用来和 grpc 进行交互, 名字跟proto文件名有相关性
python -m grpc_tools.protoc: python 下的 protoc 编译器通过 python 模块(module) 实现
--python_out=. : 编译生成处理 protobuf 相关的代码的路径, 可自定义
--grpc_python_out=. : 编译生成处理 grpc 相关的代码的路径, 可自定义
-I. helloworld.proto : proto 文件的路径
服务器端代码
# 对使用而言,若将proto文件中定义的Servicer的实现放到下面的api_rpc文件中去调用,这个文件基本是固定的,也可在服务器端实现Servicer
import time
import grpc
from concurrent import futures
from backend.camera.grpc_camera.camera_api_rpc import CameraService
from backend.camera.grpc_camera import camera_service_pb2_grpc
def serve():
print("server start!")
MAX_MESSAGE_LENGTH = 1024*1024*1024
# 定义服务器并设置最大连接数,corcurrent.futures是一个并发库,类似于线程池的概念,options定义数据传输大小,默认最大传输4M
server = grpc.server(futures.ThreadPoolExecutor(max_workers=4), options=[
('grpc.max_send_message_length', MAX_MESSAGE_LENGTH),
('grpc.max_receive_message_length', MAX_MESSAGE_LENGTH),
])
camera_service_pb2_grpc.add_CameraServicer_to_server(CameraService(), server)# 添加服务,第一个参数为定义并在文api_rpc文件中实现的server类
server.add_insecure_port('[::]:9901')
server.start()
try:
while True:
time.sleep(3600)
except KeyboardInterrupt:
server.stop(0)
客户端代码
MAX_MESSAGE_LENGTH = 1024*1024*1024
channel = grpc.insecure_channel('127.0.0.1:9901', \
options = [('grpc.max_send_message_length',MAX_MESSAGE_LENGTH), ('grpc.max_receive_message_length',MAX_MESSAGE_LENGTH)]) # 连接服务器
# with grpc.insecure_channel('127.0.0.1:9901') as self.channel:
stub = camera_service_pb2_grpc.CameraStub(self.channel) # 调用服务,即调用proto函数生成的py文件中的类
camera_info = camera_service_pb2.CameraInfo( # 构造消息体,传送参数
camera_name = args['CAMERA']["camera_name"],# 给参数赋值
camera_ip = args['CAMERA']["camera_ip"],
image_exposure = args['CAMERA']["exposure_time"],
point_cloud_exposure = args['CAMERA']["exposure_time"],
running_flag = args['CAMERA']["running_flag"],
roi = [args['CAMERA']["roi"][0], args['CAMERA']["roi"][1], args['CAMERA']["roi"][2], args['CAMERA']["roi"][3]],
gain = args['CAMERA']["gain"],
auto_gain = args['CAMERA']["auto_gain"]
)
response = stub.CreateCamera(camera_info) # 调用服务器端的函数
在api_grpc中实现proto文件中定义的Servicer
# 可以在此实现proto文件中定义的server,也可以对复杂数据类型进行转换
from backend.camera.grpc_camera import camera_service_pb2, camera_service_pb2_grpc
# 此处的定义要与proto文件中的定义名字,返回值,参数等保持一致
class CameraService(camera_service_pb2_grpc.CameraServicer):
def CreateCamera(self, request, context):
pass
return request
def RequestImage(self, request, context):
pass
def RequestImageAndPointCloud(self, request, context):
pass
设置消息大小
在打开服务器或客户端的时候设置
client:
MAX_MESSAGE_LENGTH = 256*1024*1024 # 可根据具体需求设置,此处设为256M
channel = grpc.insecure_channel(‘localhost:50051’,
options = [(‘grpc.max_send_message_length’,MAX_MESSAGE_LENGTH),
(‘grpc.max_receive_message_length’,MAX_MESSAGE_LENGTH)
])
server:
MAX_MESSAGE_LENGTH = 256*1024*1024 # 可根据具体需求设置,此处设为256M
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10), options=[
('grpc.max_send_message_length', MAX_MESSAGE_LENGTH),
('grpc.max_receive_message_length', MAX_MESSAGE_LENGTH),
] )
流式数据处理
主要使用for _ in () yield, 见开头参考文章中的内容
4 种通信方式
1. 客户端单次请求,服务端回应一次:
// Obtains the feature at a given position.
rpc GetFeature(Point) returns (Feature) {}
2. 客户端一次请求,服务端流式应答(其实相当于返回给客户端多条数据)
// Obtains the Features available within the given Rectangle. Results are
// streamed rather than returned at once (e.g. in a response message with a
// repeated field), as the rectangle may cover a large area and contain a
// huge number of features.
rpc ListFeatures(Rectangle) returns (stream Feature) {}
3. 客户端流式请求,服务端回应一次
// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
rpc RecordRoute(stream Point) returns (RouteSummary) {}
4. 客户端流式请求,服务端流式应答
// Accepts a stream of RouteNotes sent while a route is being traversed,
// while receiving other RouteNotes (e.g. from other users).
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
异常处理
踩过的坑
- 在将grpc用到软件中时,对multiprocess的manager使用不熟导致数据更新有问题
manager中dict不可拆分到不同进程中使用,跳出拆分后的进程回到主进程时,是不会将dict的值更新的。 - grpc采用客户端和服务器,就相当于一个新的进程,跟原主进程是不冲突的。
- 若在循环中调用grpc,会一直运行grpc的内容而影响主进程,可将调用变成一个线程,然后线程进入grpc久是单独进程,不会影响到主进程的更新和运行 。