rpc
RPC是远程过程调用(Remote Procedure Call)的缩写形式, 在python中, 有 基于 xml , json ,mq(zeromq) 的rpc 框架, 现在记录grpc 在 python中的使用
grpc
grpc 是一个跨语言的通用rpc框架, 比 SimpleXMLRPCServer, jsonrpclib 性能要高, 比zerorpc 支持的语言多, grpc 传输协议用的是http2 , 序列化用的是protobuf, grpc中有一元调用,单向流式调用,双向流式调用这3种调用方式
一元调用: 客户端获取调用请求, 服务器返回数据
单向流式调用: 某一端源源不断的给另一端发送数据
双向流式调用: 客户端请求服务器建立链接后,两端都可以源源不断的给对方发送数据
protobuf
protobuf 协议压缩数据比 xml json 性能高(序列化出来的二进制数据比 xml,json的小)
proto
syntax = "proto3";
option go_package = "./;proto";
message Request {
string data = 1;
}
message Reply {
string result = 1;
}
service StreamGrpc{
// 一元调用
rpc GetServerResult(Request) returns (Reply){}
// 单向流 服务器返回流数据
rpc ServerStream(Request) returns (stream Reply){}
// 单向流 客户端发送流数据
rpc ClientStream(stream Request) returns (Reply){}
// 双向流 服务器和客户端都发送流数据
rpc ServerClientStream(stream Request) returns (stream Reply){}
}
protobuf生成python rpc文件命令
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. stream_grpc.proto
server
import time
from concurrent import futures
from threading import Thread
import grpc
from rpc.stream_grpc.proto import streamGrpc_pb2_grpc, streamGrpc_pb2
def server_send_stream():
for i in range(10):
yield streamGrpc_pb2.Reply(result="server send stream {}".format(i))
time.sleep(1)
def server_recv_stream(request_iterator):
for i in request_iterator:
print("server 接收到的数据是: ", i)
class StreamServer(streamGrpc_pb2_grpc.StreamGrpcServicer):
def GetServerResult(self, request, context):
print("接收到grpc 请求数据是: ", request.data)
return streamGrpc_pb2.Reply(result="这是一元调用")
def ServerStream(self, request, context):
print("serverstream 接收到的数据是: ", request.data)
for i in range(10):
yield streamGrpc_pb2.Reply(result="hello {}".format(i))
time.sleep(1)
def ClientStream(self, request_iterator, context):
for i in request_iterator:
print("clientStream 接收到的数据是: ", i)
print("client stream end")
return streamGrpc_pb2.Reply(result="client stream end ")
def ServerClientStream(self, request_iterator, context):
# 创建服务器接收流数据线程, 通过多线程的方式实现同时发送和接收数据
t = Thread(target=server_recv_stream, args=(request_iterator,))
t.start()
for i in range(2):
yield streamGrpc_pb2.Reply(result="server send stream {}".format(i))
time.sleep(1)
t.join()
def main():
g = grpc.server(thread_pool=futures.ThreadPoolExecutor(max_workers=10))
g.add_insecure_port("0.0.0.0:9966")
streamGrpc_pb2_grpc.add_StreamGrpcServicer_to_server(StreamServer(), g)
g.start()
g.wait_for_termination()
if __name__ == '__main__':
main()
client
import time
from concurrent import futures
import grpc
from rpc.stream_grpc.proto import streamGrpc_pb2, streamGrpc_pb2_grpc
# 迭代器生成数据
def ClientSendStream():
for i in range(10):
yield streamGrpc_pb2.Request(data="client send: {}".format(i))
time.sleep(1)
class ClientRpc:
def __init__(self):
self.channel = grpc.insecure_channel("127.0.0.1:9966")
self.client = streamGrpc_pb2_grpc.StreamGrpcStub(channel=self.channel)
def GetServerResult(self):
result = self.client.GetServerResult(streamGrpc_pb2.Request(data="test"))
print(result.result)
# 单向流式调用, 接收服务器流数据
def ServerStream(self):
result = self.client.ServerStream(streamGrpc_pb2.Request(data="test"))
for i in result:
print("server stream result: ", i)
# 单向流式调用, 给服务器发送流式数据, 通过迭代器的方式发送
def ClientStream(self):
# 执行到这里的时候会堵塞
result = self.client.ClientStream(ClientSendStream())
print("Client stream result: ", result)
# 双向流式调用, 服务器可以给客户端发送,客户端也可以给服务器发送流式数据
def ServerClientStream(self):
# 这里 grpc 应该是生成了一个线程用来发送数据
result = self.client.ServerClientStream(ClientSendStream())
for i in result:
print("client server stream result : ", i)
if __name__ == '__main__':
client = ClientRpc()
th = futures.ThreadPoolExecutor(max_workers=10)
f1 = th.submit(client.GetServerResult)
f2 = th.submit(client.ServerStream)
f3 = th.submit(client.ClientStream)
f4 = th.submit(client.ServerClientStream)
task_list = [f1, f2, f3, f4]
futures.wait(task_list)