心法利器[6] | python grpc实践

内容安全与风控智能语音交互智能体验与创作

【 前沿重器 】

全新栏目,本栏目主要和大家一起讨论近期自己学习的心得和体会,与大家一起成长。具体介绍:仓颉专项:飞机大炮我都会,利器心法我还有

往期回顾

完成一个算法任务后,总需要将服务打包上线,上线需要的一个人物,就是给包裹一层服务,对于我们常用的python而言,flask、tornado等都是比较常用的,今天给大家推荐的是一个比较新潮而且已经逐步推开使用的方案——grpc,这个东西其实我之间已经介绍过,并且给大家跑通了c++版本的demo,可能受限于语言,阅读量并不是特别好,对于它的基础知识,还是建议大家看下:

今天就来给大家分享这套方案,具体的demo代码来自:https://zhuanlan.zhihu.com/p/97893141,我已经跑通,我会再更详细地聊聊里面的内容。

grpc再介绍

grpc是一套谷歌开发的rpc框架https://github.com/grpc/grpc(相似的还有百度的brpc等https://github.com/apache/incubator-brpc/blob/master/README\_cn.md),grpc提供了C、C++、Ruby、Python、Java等多种语言的接口,所以通用性很强,而且对于网络协议,其实不需要相同语言支持,例如python启动了一个grpc服务,Java只要按照proto约定的接口发出请求,那就可以收到并处理。

protobuf

protobuf也是谷歌出的(https://github.com/protocolbuffers/protobuf),是一种类似json、xml的结构,而相比这两个,其在效率、性能上有更高,作网络通信、数据交换有非常高效。

有关这块的语法,简单地可以通过这个教程来看看,其实就是比json格式多了一些数据类型的定义。

这里需要详细说的其实是有关grpc方面的数据类型。先来看grpc的官方例子——hello world任务的proto和核心部分:


          
// The greeting service definition.  
service Greeter {  
  // Sends a greeting  
  rpc SayHello (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;  
}  

      

最核心的其实就是 service 类型,这个类型的核心就在于我们需要定义服务,输入什么,输出什么。 rpc 本身代表了服务的类型,输入和输出的结构通常用message来表示,其实就是一个约束了数据类型的json格式了。

python构造grpc

环境

首先还是先把python的grpc环境构造起来:


          
pip install grpcio -i ${PIPSOURCE}  
pip install grpcio-tools -i ${PIPSOURCE}  
pip install protobuf -i ${PIPSOURCE}  

      

PIPSOURCE 是自己定义的pip源,设置国内的源,如阿里云、清华等,下载速度会加快很多。

proto

proto文件本身其实和python无关,只是一个和c++语法类似的配置文件。

下面的代码例子来自https://zhuanlan.zhihu.com/p/97893141,这个例子里实现了两个任务,一个是加法,一个是乘法,先来看看pb文件:


          
syntax = "proto3";  
  
service Cal {  
  rpc Add(AddRequest) returns (ResultReply) {}  
  rpc Multiply(MultiplyRequest) returns (ResultReply) {}  
}  
  
message AddRequest {  
  int32 number1  = 1;  
  int32 number2  = 2;  
}  
  
message MultiplyRequest {  
  int32 number1  = 1;  
  int32 number2  = 2;  
}  
  
message ResultReply {  
  int32 number = 1;  
}  

      

输入是两个数,这里约束的都是32位整型。

然后就可以来编译proto文件为具体的python文件了:


          
 python3 -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./SimpleCal.proto  

      

编译完以后就会多两个文件:


          
SimpleCal_pb2_grpc.py  SimpleCal_pb2.py  

      

前者是针对服务的,即客户端类和服务端类,而后者则是针对request和response的,有兴趣可以help(XXX)一波看看概览。

服务端

有了上面的东西,就可以开始写自己的服务了,直接上代码:


          
from concurrent import futures  
import grpc  
import SimpleCal_pb2  
import SimpleCal_pb2_grpc  
  
class CalServicer(SimpleCal\_pb2\_grpc.CalServicer):  
  def Add(self, request, context):   # Add函数的实现逻辑  
    print("Add function called")  
    return SimpleCal_pb2.ResultReply(number=request.number1 + request.number2)  
  
  def Multiply(self, request, context):   # Multiply函数的实现逻辑  
    print("Multiply service called")  
    return SimpleCal_pb2.ResultReply(number=request.number1 * request.number2)  
  
def serve():  
  server = grpc.server(futures.ThreadPoolExecutor(max_workers=5))  
  SimpleCal_pb2_grpc.add_CalServicer_to_server(CalServicer(),server)  
  server.add_insecure_port("[::]:50051")  
  server.start()  
  print("grpc server start...")  
  server.wait_for_termination()  
  
if __name__ == '\_\_main\_\_':  
  serve()  

      

首先是这个服务 CalServices 他继承于SimpleCal_pb2_grpc的CalServicer,就需要定义里面的计算,其实这里的定义很简单,以加法为例,重点在这句:


          
return SimpleCal_pb2.ResultReply(number=request.number1 + request.number2)  

      

这里写的很简单,其实就是直接拿request里面的两个东西进行计算罢了,这个ResultReply可以把对应的内容转化为pb完成回复。

这个类用于构造服务,服务是通过构造grpc.server后,在通过add_CalServicer_to_server把这个server对象加上,在配置端口后,服务就算启动了,核心就是这么几行:


          
server = grpc.server(futures.ThreadPoolExecutor(max_workers=5))  
SimpleCal_pb2_grpc.add_CalServicer_to_server(CalServicer(),server)  
server.add_insecure_port("[::]:50051")  
server.start()  

      

由于服务是一个类似监控的任务,是持续运行的,所以运行的时候,最好是使用nohup来执行:


          
nohup python cal_server.py &  

      

如果执行成功,应该可以用 ps -ef | grep cal\_server 来看这个任务还在不在。

完事了把这个服务kill调就行。

客户端

客户端是请求服务端获得结果的,所以简单地,可以只需要把pb中提到的请求内容请求出去就好了。


          
import SimpleCal_pb2  
import SimpleCal_pb2_grpc  
import grpc  
  
def run(n, m):  
  channel = grpc.insecure_channel('localhost:50051') # 连接上gRPC服务端  
  stub = SimpleCal_pb2_grpc.CalStub(channel)  
  response = stub.Add(SimpleCal_pb2.AddRequest(number1=n, number2=m))  # 执行计算命令  
  print(f"{n} + {m} = {response.number}")  
  response = stub.Multiply(SimpleCal_pb2.MultiplyRequest(number1=n, number2=m))  
  print(f"{n} * {m} = {response.number}")  
  
if __name__ == "\_\_main\_\_":  
  run(100, 300)  

      

首先构造一个请求的通道,通过这个通道构造一个stub,这个东西其实和http中的client类似,然后就可以发送请求了,分别是加和乘。

这里面没有复杂的操作,其实就是整理好数据,然后把数据放入通道就行,其实就和函数一样,调用后就能得到结果了。这个执行就没那么复杂了。


          
python cal_client.py  

      

小结

记录的是自己周末的学习,简单的走通了demo,后续要根据自己的需求修改pb,修改请求,尤其是服务端,工程化流程化。

趁着文章能写完,给大家分享一下,构造服务的能力也是算法工程师非常必要的一个能力,没有这个,你的算法根本无法上线。

picture.image

0
0
0
0
关于作者
关于作者

文章

0

获赞

0

收藏

0

相关资源
火山引擎大规模机器学习平台架构设计与应用实践
围绕数据加速、模型分布式训练框架建设、大规模异构集群调度、模型开发过程标准化等AI工程化实践,全面分享如何以开发者的极致体验为核心,进行机器学习平台的设计与实现。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论