流程

img

img

环境准备

1
pip3 install grpcio grpcio-tools

定义服务

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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方法:AddMultiply,需要分别在gRPC的服务端中实现加法和乘法。
  • 同时我们也定义了2个方法的参数,Add方法的参数是AddRequest,包含number1和number2两个整数参数。 Multiply方法的参数是MultiplyRequest,里面也有number1和number2两个整数参数。两个函数的返回结构都是ResultReply,内容是一个整数。

message 是代表数据结构(里面可以包括不同类型的成员变量,包括字符串、数字、数组、字典……),service代表 RPC 接口。变量后面的数字是代表进行二进制编码时候的提示信息,1~15 表示热变量,会用较少的字节来编码。

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

1
2
3
$ 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.pySimpleCal_pb2.py这2个文件。这2个文件会在后面的客户端和服务端代码中被引用。

服务端和客户端样例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
# 服务端代码 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类中对AddMultiply两个方法的实现。
  • 逻辑很简单,从request中读取number1和number2,然后相加。注意,这里的所有变量都需要完整名称:request.number1和request.number2, 不能使用位置参数Multiply 的实现和Add一样,不多说了。
  • serve函数里定义了gRPC的运行方式,使用5个worker的线程池。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 客户端代码 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服务,结果如下:

1
2
3
4
$ python3 cal_server.py  &
$ python3 cal_client.py
100 + 300 = 400
100 * 300 = 30000