python实现grpc

背景

python作为一种胶水语言,天然的是很好的中间件的语言首选。python拥有强大的兼容性,尤其是在科学计算领域,可以灵活的调用c++的dll库,调用pytorch和tensorflow等python算法框架。因此在我的项目中,使用python3.7作为算法中间件的实现语言,集成各类算法,对外提供服务。当然,这种方案也是有弊端的,就是python的性能,毕竟不如c++,但是相比较综合的使用成本,是优于c++的。

参考资料

https://www.zhihu.com/question/41609070/answer/1030913797 既然有 HTTP 请求,为什么还要用 RPC 调用? - 易哥的回答 - 知乎
https://zhuanlan.zhihu.com/p/36427583 如何给老婆解释什么是RPC - 柳树的文章 - 知乎
https://www.grpc.io/ grpc官网
https://developers.google.com/protocol-buffers protobuf官网
https://blog.csdn.net/qq_25310669/article/details/120651472 java实现grpc,与这一篇是对应的。
https://www.grpc.io/docs/languages/python/quickstart/ python的快速开始

案例与解决方案

1.安装grpc相关的依赖、代码案例

1.先决条件:
Python 3.5 或更高版本
pip 9.0.1 或更高版本
2.安装grpcio,protobuf

python -m pip install grpcio
python -m pip install protobuf

3.安装grpcio-tools,用于基于proto文件生成python代码。

python -m pip install grpcio-tools

4.克隆一下案例代码进行学习

$ git clone -b v1.41.0 https://github.com/grpc/grpc
# Navigate to the "hello, world" Python example:
$ cd grpc/examples/python/helloworld

2.设计proto文件

此处使用博主这篇博文的proto文件的内容(https://blog.csdn.net/qq_25310669/article/details/120651472),proto文件内容如下:

 syntax = "proto3";


option java_multiple_files = false; //不要拆分成多个文件
option java_package = "com.rpc.yolo";
option java_outer_classname = "YoloProto";


// yolo模型对外提供的接口
service YoloFun {
  // 初始化
  rpc init (InitRequest) returns (InitReply) {}
  // 检测
  rpc detection (DetectionRequest) returns (DetectionReply) {}
  // 模拟方法
  rpc free (FreeRequest) returns (FreeReply) {}
}

// 初始化方法发送对象
message InitRequest {
  string id = 1;
  string cfg = 2;
  string data = 3;
  string weights= 4;
}
// 初始化方法反馈对象
message InitReply {
  string result = 1;
}

// 检测方法发送对象
message DetectionRequest {
  string id = 1; //模型编号
  string path = 2; //原图的文件路径 /usr/img/1.jpg
  string detect_file_path = 3; //识别后的文件路径 /usr/local/1.jpg
}
//怕掉精度,直接存string
message Box{
  string x = 1;
  string y = 2;
  string w = 3;
  string h = 4;
  string obj = 5;
  string prob = 6;
}
// 检测方法反馈对象
message DetectionReply {
  string msg = 1;
  repeated Box boxes = 2;
}

// 释放方法发送对象
message FreeRequest {
  string id = 1;
}
// 释放方法反馈对象
message FreeReply {
  string result = 1;
}


3.基于proto,生成python代码

管理员权限打开终端,切到proto文件所在的路径,然后执行protoc命令对文件进行编译,生成代码:

python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. yolo.proto

yolo.proto 是文件名,输出到当前目录。
然后将生成的文件拷贝到需要的目录就可以使用了。生成的文件截图如下:
在这里插入图片描述
yolo_pb2:基本是参数相关的在里面。
yolo_pb2_grpc:是接口相关的在里面。
使用的时候按照这个逻辑去做就ok了。

4.python实现rpc服务

首先对暴露的接口进行具体的实现,具体的语法可以看quick-start,案例如下:

 """
[yolo模型对外暴露的方法]
"""
from . import yolo_pb2
from . import yolo_pb2_grpc
import logging
import grpc
from . import darknet_images


class Yolo(yolo_pb2_grpc.YoloFunServicer):

    async def init(
            self, request: yolo_pb2.InitRequest,
            context: grpc.aio.ServicerContext) -> yolo_pb2.InitReply:
        """[初始化模型]

        Args:
            request (yolo_pb2.InitRequest): [初始化请求]
            context (grpc.aio.ServicerContext): [上下文]

        Returns:
            yolo_pb2.InitReply: [初始化结果]
        """
        logging.debug('Init~~')
        # 异常
        try:
          darknet_images.init_network(request.id, request.cfg, request.data, request.weights)
        except BaseException:
          logging.error('初始化发生了异常!')
          return yolo_pb2.InitReply(result='error')      
        return yolo_pb2.InitReply(result='success')
    
    async def detection(
            self, request: yolo_pb2.DetectionRequest,
            context: grpc.aio.ServicerContext) -> yolo_pb2.DetectionReply:
        logging.debug('Detection~~')
        detections = darknet_images.detection_img(request.id, request.path,request.detect_file_path)
        detectionReply = yolo_pb2.DetectionReply()
        # 判断非空
        if detections == None:
            # box = detectionReply.boxes.add()
            # box.obj = str('') 
            # box.prob = str('') 
            # box.x = str('') 
            # box.y = str('') 
            # box.w = str('') 
            # box.h = str('') 
            # none表示未初始化
            detectionReply.msg = 'none' 
            return detectionReply

        for item in detections:
            box = detectionReply.boxes.add()
            box.obj = str(item[0]) 
            box.prob = str(item[1])
            box.x = str(item[2][0])
            box.y = str(item[2][1])
            box.w = str(item[2][2])
            box.h = str(item[2][3])
        return detectionReply

    async def free(
            self, request: yolo_pb2.FreeRequest,
            context: grpc.aio.ServicerContext) -> yolo_pb2.FreeReply:
        """释放模型

        Args:
            request (yolo_pb2.FreeRequest): [请求内容,含有id]
            context (grpc.aio.ServicerContext): [上下文]

        Returns:
            yolo_pb2.FreeReply: [释放结果]
        """
        logging.debug('Free,模型编号:' + request.id)
        # 异常
        try:
          darknet_images.free_network(request.id)
        except BaseException:
          logging.error('释放模型发生了异常!')
          return yolo_pb2.FreeReply(result='error')
        logging.debug('模型释放成功!')      
        return yolo_pb2.FreeReply(result='success')

5.服务对外暴露

有了接口代码,还需要把接口暴露出去。一般在这个地方可以集成多个rpc服务,不只是一个rpc服务文件的暴露。

 """python算法中间件启动主程序"""
import sys
import os
sys.path.append(os.path.dirname(__file__) + "/AlgorithmsRepo/yolo")

from AlgorithmsRepo.yolo.yolo_service import Yolo
import logging
import asyncio
import grpc
# from AlgorithmsRepo.yolo import yolo_pb2_grpc
import AlgorithmsRepo.yolo.yolo_pb2_grpc as yolo_pb2_grpc

 

async def serve() -> None:
    server = grpc.aio.server()
    #在这里添加对外公布的接口类
    yolo_pb2_grpc.add_YoloFunServicer_to_server(Yolo(), server)
    listen_addr = '[::]:22972'
    server.add_insecure_port(listen_addr)
    logging.info("Starting server on %s", listen_addr)
    await server.start()
    try:
        await server.wait_for_termination()
    except KeyboardInterrupt:
        # Shuts down the server with 0 seconds of grace period. During the
        # grace period, the server won't accept new connections and allow
        # existing RPCs to continue within the grace period.
        await server.stop(0)


if __name__ == '__main__':
    logging.basicConfig(level=logging.DEBUG)
    asyncio.run(serve())

Python可以使用gRPC实现通信,gRPC是一种高性能、开源的远程过程调用(RPC)框架。它使用Protocol Buffers作为接口定义语言(IDL),支持多种编程语言。 要在Python实现gRPC通信,需要进行以下步骤: 1. 定义服务接口:使用Protocol Buffers定义服务接口,包括请求和响应的消息类型以及服务方法。 2. 生成代码:使用Protocol Buffers编译器将定义的.proto文件生成对应Python代码。 3. 实现服务逻辑:编写服务实现类,继承自生成的代码中的服务基类,并实现定义的服务方法。 4. 启动服务器:创建gRPC服务器,并将实现的服务添加到服务器中。 5. 创建客户端:创建gRPC客户端,通过客户端调用远程服务方法。 下面是一个简单的示例: 1. 定义服务接口(example.proto): ``` syntax = "proto3"; package example; service Greeter { rpc SayHello (HelloRequest) returns (HelloResponse) {} } message HelloRequest { string name = 1; } message HelloResponse { string message = 1; } ``` 2. 生成代码:使用Protocol Buffers编译器生成Python代码: ``` $ python -m grpc_tools.protoc -I . --python_out=. --grpc_python_out=. example.proto ``` 3. 实现服务逻辑(server.py): ```python import grpc import example_pb2 import example_pb2_grpc class GreeterServicer(example_pb2_grpc.GreeterServicer): def SayHello(self, request, context): message = f"Hello, {request.name}!" return example_pb2.HelloResponse(message=message) def serve(): server = grpc.server(futures.ThreadPoolExecutor(max_workers=10)) example_pb2_grpc.add_GreeterServicer_to_server(GreeterServicer(), server) server.add_insecure_port('[::]:50051') server.start() server.wait_for_termination() if __name__ == '__main__': serve() ``` 4. 启动服务器: ``` $ python server.py ``` 5. 创建客户端(client.py): ```python import grpc import example_pb2 import example_pb2_grpc def run(): channel = grpc.insecure_channel('localhost:50051') stub = example_pb2_grpc.GreeterStub(channel) response = stub.SayHello(example_pb2.HelloRequest(name='Alice')) print("Greeter client received: " + response.message) if __name__ == '__main__': run() ``` 以上是一个简单的Python实现gRPC通信的示例。你可以根据自己的需求定义更复杂的服务接口和实现逻辑。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值