grpc python3_用 Python 编写简单的 gRPC 服务

作者:xnow.me

Blog: zhihu.com/people/xnow.me

个人感觉gRPC一直在流行与不流行之间,似乎周围没什么人用,但是每隔一段时间都会听到一些gRPC的消息,今天刚好有团队要gRPC的支持,所以就自己也测试下怎么用Python来写gRPC的服务。RPC是远程过程调用(Remote Procedure Call)的缩写形式,可以理解为RPC就是要像调用本地的函数一样去调远程函数,gRPC就是Google开源的RPC框架。

这里写个简单的Python gRPC示例,能实现加法和乘法的计算器:

版本信息:

Python 3.6.8

grpcio 1.25.0

grpcio-tools 1.25.0

nginx version: nginx/1.14.0

开始环境准备

安装gRPC相关的库,grpcio-tools主要用根据我们的protocol buffer定义来生成Python代码,官方解释是Protobuf code generator for gRPC。protocolbuffers/protobuf是Google开发的一种序列化数据结构的协议。具体结构和语法超纲了,现在还不多用做太多理解,只要会用就行了。

$ sudo pip3 install grpcio grpcio-tools

定义服务:使用protocolbuffers/protobuf格式来创建结构化数据文件SimpleCal.proto,内容如下:

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;

}

在SimpleCal.proto 文件中定义了一个服务Cal,定义了2个RPC方法:Add和Multiply,需要分别在gRPC的服务端中实现加法和乘法。

同时我们也定义了2个方法的参数,Add方法的参数是AddRequest,包含number1和number2两个整数参数。Multiply方法的参数是MultiplyRequest,里面也有number1和number2两个整数参数。两个函数的返回结构都是ResultReply,内容是一个整数。

根据上面的定义,生成Python代码:

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

$ ls

SimpleCal_pb2_grpc.py  SimpleCal_pb2.py  SimpleCal.proto

使用python3 -m grpc_tools.protoc --hel能获得命令的参数含义。ls可以看到grpc_tools 帮我们自动生成了 SimpleCal_pb2_grpc.py, SimpleCal_pb2.py这2个文件。这2个文件会在后面的客户端和服务端代码中被引用。

服务端和客户端样例

下面是服务端代码 cal_server.py:

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()

这里的重点在于CalServicer类中对Add和Multiply两个方法的实现。逻辑很简单,从request中读取number1和number2,然后相加。注意,这里的所有变量都需要完整名称:request.number1和request.number2, 不能使用位置参数。Multiply 的实现和Add一样,不多说了。serve函数里定义了gRPC的运行方式,使用5个worker的线程池。

客户端代码 cal_client.py :

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)

客户端的逻辑更加简单,就连上gRPC服务,然后发起调用。下面开启服务端,并执行客户端代码调用gRPC服务,结果如下:

$ python3 cal_server.py  &

$ python3 cal_client.py

100 + 300 = 400

100 * 300 = 30000

执行结果表明客户端和服务端已经都运行正常。更多的gRPC样例可以访问gRPC官网的Example, grpc/grpc 。

https://github.com/grpc/grpc/tree/master/examples/python

使用Nginx来代理gRPC

gRPC是基于HTTP/2协议的,Nginx在1.9.5里开始支持HTTP/2,在1.13.10里开始支持gRPC。为了反向代理gRPC服务,编译Nginx的时候必须要添加这两个参数:--with-http_ssl_module --with-http_v2_module

给Nginx添加如下的server配置:

server {

listen 80 http2;

location / {

grpc_pass grpc://localhost:50051;

}

}

把这段server的配置添加到Nginx的http段里,配置和启动好Nginx之后,然后把cal_client.py里的channel = grpc.insecure_channel('localhost:50051') 一行的连接地址替换为Nginx提供的地址就可以了。执行结果是一样的,就不再做一遍了。

接着往下挖掘gRPC的HTTP2.0接口细节的话,可以打开SimpleCal_pb2_grpc.py你可以看到在CalStub这个类的__init__方法里,定义了Add和Multiply两个函数对应的uri。

class CalStub(object):

# missing associated documentation comment in .proto file

pass

def __init__(self, channel):

"""Constructor.

Args:

channel: A grpc.Channel.

"""

self.Add = channel.unary_unary(

'/Cal/Add',           # 这个是对应Add方法的http url地址

request_serializer=SimpleCal__pb2.AddRequest.SerializeToString,

response_deserializer=SimpleCal__pb2.ResultReply.FromString,

)

self.Multiply = channel.unary_unary(

'/Cal/Multiply',     # 这个是对应Multiply方法的http url地址

request_serializer=SimpleCal__pb2.MultiplyRequest.SerializeToString,

response_deserializer=SimpleCal__pb2.ResultReply.FromString,

)

查看Nginx的日志也能表明这一点:

127.0.0.1 - - [18/Nov/2019:20:09:25 +0800] "POST /Cal/Add HTTP/2.0" 200 8 "-" "grpc-python/1.25.0 grpc-c/8.0.0 (manylinux; chttp2; game)"

127.0.0.1 - - [18/Nov/2019:20:09:25 +0800] "POST /Cal/Multiply HTTP/2.0" 200 9 "-" "grpc-python/1.25.0 grpc-c/8.0.0 (manylinux; chttp2; game)"

如果部署了多个gRPC服务端,也可以使用Nginx的upstream来做多个后端的负载均衡。

最后,用wireshark来对http2的流量进行抓包分析。

抓取HTTP2的数据包进行gRPC协议分析

参考文章:

Introducing gRPC Support with NGINX 1.13.10 - NGINX

https://www.nginx.com/blog/nginx-1-13-10-grpc/

gRPC 官方文档中文版_V1.0

https://doc.oschina.net/grpc?t=60138

grpc/grpc

https://github.com/grpc/grpc/tree/master/examples/python

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值