gRPC 一开始由 google 开发,是一款语言中立、平台中立、开源的远程过程调用(RPC)系统。java
在 gRPC 里客户端应用能够像调用本地对象同样直接调用另外一台不一样的机器上服务端应用的方法,使得您可以更容易地建立分布式应用和服务。与许多 RPC 系统相似,gRPC 也是基于如下理念:定义一个服务,指定其可以被远程调用的方法(包含参数和返回类型)。在服务端实现这个接口,并运行一个 gRPC 服务器来处理客户端调用。在客户端拥有一个存根可以像服务端同样的方法。由于 gRPC 对 HTTP/2 协议的支持使其在 Android、IOS 等客户端后端服务的开发领域具备良好的前景。gRPC 提供了一种简单的方法来定义服务,同时客户端能够充分利用 HTTP2 stream 的特性,从而有助于节省带宽、下降 TCP 的链接次数、节省CPU的使用等。python
基于HTTP/2 nginx
IDL使用ProtoBuf json
多语言支持 ( C, C++, Python, PHP, Nodejs, C#, Objective-C、Golang、Java)后端
gRPC已经应用在Google的云服务和对外提供的API中,其主要应用场景以下: 服务器
protobuf二进制消息,性能好/效率高(空间和时间效率都很不错)
proto文件生成目标代码,简单易用
序列化反序列化直接对应程序中的数据类,不须要解析后在进行映射(XML,JSON都是这种方式)
支持向前兼容(新加字段采用默认值)和向后兼容(忽略新加字段),简化升级
支持多种语言(能够把proto文件看作IDL文件) 并发
Netty等一些框架集成负载均衡
GRPC还没有提供链接池,须要自行实现 框架
还没有提供“服务发现”、“负载均衡”机制 分布式
由于基于HTTP2,绝大部多数HTTP Server、Nginx都尚不支持,即Nginx不能将GRPC请求做为HTTP请求来负载均衡,而是做为普通的TCP请求。(nginx1.9版本已支持)
http2只容许单个连接传输10亿流数据。缘由在于: htt2使用31位×××标示流,服务端使用奇数,客户端使用偶数,因此总共10亿可用
解决思路:超过必定数量的流,须要重启连接。
gRPC有四种通讯方式:
简单rpc 这就是通常的rpc调用,一个请求对象对应一个返回对象
proto语法:
rpc simpleHello(Person) returns (Result) {}
服务端流式rpc 一个请求对象,服务端能够传回多个结果对象
proto语法 :
rpc serverStreamHello(Person) returns (stream Result) {}
客户端流式rpc 客户端传入多个请求对象,服务端返回一个响应结果
proto语法 :
rpc clientStreamHello(stream Person) returns (Result) {}
双向流式rpc 结合客户端流式rpc和服务端流式rpc,能够传入多个对象,返回多个响应对象
proto语法 :
rpc biStreamHello(stream Person) returns (stream Result) {}
gRPC使用ProtoBuf定义服务, 咱们能够一次性的在一个 .proto 文件中定义服务并使用任何支持它的语言去实现客户端和服务器,反过来,它们能够在各类环境中,从云服务器到你本身的平板电脑—— gRPC 帮你解决了不一样语言及环境间通讯的复杂性。使用 protocol buffers 还能得到其余好处,包括高效的序列号,简单的 IDL 以及容易进行接口更新。
gRPC 的安装: pip install grpcio
安装 ProtoBuf 相关的 python 依赖库: pip install protobuf
安装 python grpc 的 protobuf 编译工具: pip install grpcio-tools
syntax = "proto3"; package example; service FormatData { //定义服务,用在rpc传输中 rpc DoFormat(actionrequest) returns (actionresponse){} } message actionrequest { string text = 1; } message actionresponse{ string text=1; }
在proto文件目录下 调用下列命令
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. ./data.proto
会生成:data_pb2.py 与 data_pb2_grpc.py, 其中data_pb2.py是数据格式调用的文件,data_pb2_grpc.py是grpc传输协议接口调用的文件.
在服务器端代码中须要实现proto文件中编写的服务接口,并重写处理函数,将重写后的服务类实例化之后添加到grpc服务器中,这样建立的grpc服务器就能够实现自定义的proto传输服务了
# 实现了 server 端用于接收客户端发送的数据,并对数据进行大写处理后返回给客户端 # ! /usr/bin/env python # -*- coding: utf-8 -*- import grpc import time from concurrent import futures from example import data_pb2, data_pb2_grpc _ONE_DAY_IN_SECONDS = 60 * 60 * 24 _HOST = 'localhost' _PORT = '8080' import json # 实现一个派生类,重写rpc中的接口函数.自动生成的grpc文件中比proto中的服务名称多了一个Servicer class FormatData(data_pb2_grpc.FormatDataServicer): # 重写接口函数.输入和输出都是proto中定义的Data类型 def DoFormat(self, request, context): str = request.text print(str, type(str)) return data_pb2.actionresponse(text=json.dumps(str.upper())) # 返回一个类实例 def serve(): # 定义服务器并设置最大链接数,corcurrent.futures是一个并发库,相似于线程池的概念 grpcServer = grpc.server(futures.ThreadPoolExecutor(max_workers=4)) # 建立一个服务器 data_pb2_grpc.add_FormatDataServicer_to_server(FormatData(), grpcServer) # 在服务器中添加派生的接口服务(本身实现了处理函数) grpcServer.add_insecure_port(_HOST + ':' + _PORT) # 添加监听端口 grpcServer.start() # 启动服务器 try: while True: time.sleep(_ONE_DAY_IN_SECONDS) except KeyboardInterrupt: grpcServer.stop(0) # 关闭服务器 if __name__ == '__main__': serve()
# 实现了客户端用于发送数据并打印接收到 server 端处理后的数据 # ! /usr/bin/env python # -*- coding: utf-8 -*- import grpc from example import data_pb2, data_pb2_grpc _HOST = 'localhost' _PORT = '8080' import json def run(): conn = grpc.insecure_channel(_HOST + ':' + _PORT) # 监听频道 print(conn) client = data_pb2_grpc.FormatDataStub(channel=conn) # 客户端使用Stub类发送请求,参数为频道,为了绑定连接 print(client) data = {'name': 'xjt', 'age': 18} response = client.DoFormat(data_pb2.actionrequest(text=json.dumps(data))) # 返回的结果就是proto中定义的类 print("received: " + response.text) if __name__ == '__main__': run()
客户端连接的主机号和端口号,必须是服务器建立的主机号和端口号.
先运行服务端,在运行客户端,结果以下:
client.py
server.py
最终目录结构