什么是RPC服务 RPC,是Remote Procedure Call的简称,翻译成中文就是远程过程调用。RPC就是容许程序调用另外一个地址空间(一般是另外一台机器上)的类方法或函数的一种服务。 它是一种架设在计算机网络之上并隐藏底层网络技术,能够像调用本地服务同样调用远端程序,在编码代价不高的状况下提高吞吐的能力。java
为何要使用RPC服务 随着计算机技术的快速发展,单台机器运行服务的方案已经不足以支撑愈来愈多的网络请求负载,分布式方案开始兴起,一个业务场景能够被拆分在多个机器上运行,每一个机器分别只完成一个或几个的业务模块。为了能让其余机器使用某台机器中的业务模块方法,就有了RPC服务,它是基于一种专门实现远程方法调用的协议上完成的服务。现现在不少主流语言都支持RPC服务,经常使用的有Java的Dubbo、Go的net/rpc & RPCX、谷歌的gRPC等。python
关于gRPC 大部分RPC都是基于socket实现的,能够比http请求来的高效。gRPC是谷歌开发并开源的一款实现RPC服务的高性能框架,它是基于http2.0协议的,目前已经支持C、C++、Java、Node.js、Python、Ruby、Objective-C、PHP和C#等等语言。要将方法调用以及调用参数,响应参数等在两个服务器之间进行传输,就须要将这些参数序列化,gRPC采用的是protocol buffer的语法(检查proto),经过proto语法能够定义好要调用的方法、和参数以及响应格式,能够很方便地完成远程方法调用,并且很是利于扩展和更新参数。数据库
使用gRPC实现远程方法调用以前,咱们须要了解protocol buffer语法,安装支持protocol buffer语法编译成.proto文件的工具,而后再完成gRPC的服务端(远程方法提供者)和客户端(调用者)的搭建和封装。json
Protocol Buffer是Google的跨语言,跨平台,可扩展机制的,用于序列化结构化数据 - 对比XML,但更小,更快,更简单的一种数据格式。您能够定义数据的结构化,例如方法的名字、参数和响应格式等,而后可使用对应的语言工具生成的源代码轻松地在各类数据流中使用各类语言编写和读取结构化数据。api
package test;
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
}
复制代码
上面的例子就是一个.proto文件,该文件的第一行指定包名,方便您在别的proto文件中import这个文件的定义,第二行是您正在使用proto3语法:若是您不这样作,protobuf 编译器将假定您正在使用proto2。这必须是文件的第一个非空的非注释行,目前建议使用proto3语法。 SearchRequest是消息体的名字,指定了三个字段,分别指定了字段的类型和顺序,顺序必须从1开始,而且不可重复;安全
单数(默认):格式良好的消息能够包含该字段中的零个或一个(但不超过一个)。 repeated:此字段能够在格式良好的消息中重复任意次数(包括零)。将保留重复值的顺序。例如:ruby
syntax = "proto3";
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
repeated Body body = 4;
}
message Body {
int32 id = 1;
string number = 2;
}
复制代码
上述例子其实就是定义了一个格式,用咱们一般的json格式表示就是:bash
{
"query": str,
"page_number":int,
"result_per_page":int,
"body":[
{
"id":int,
"number":str
}
],
}
复制代码
.proto Type | 备注 | Python Typ |
---|---|---|
double | float | |
float | float | |
int32 | 使用变长编码,对于负值的效率很低,若是你的域有可能有负值,请使用sint64替代 | int |
uint32 | 使用变长编码 | int/long |
uint64 | 使用变长编码 | int/long |
sint32 | 使用变长编码,这些编码在负值时比int32高效的多 | int |
sint64 | 使用变长编码,有符号的整型值。编码时比一般的int64高效。 | int/long |
fixed32 | 老是4个字节,若是数值老是比老是比228大的话,这个类型会比uint32高效。 | int |
fixed64 | 老是8个字节,若是数值老是比老是比256大的话,这个类型会比uint64高效。 | int/long |
sfixed32 | 老是4个字节 | int |
sfixed64 | 老是8个字节 | int/long |
bool | 布尔值 | bool |
string | 一个字符串必须是UTF-8编码或者7-bit ASCII编码的文本。 | str/unicode |
bytes | 可能包含任意顺序的字节数据。 | str |
message SearchRequest {
string query = 1;
int32 page_number = 2;
int32 result_per_page = 3;
enum Corpus {
UNIVERSAL = 0;
WEB = 1;
IMAGES = 2;
LOCAL = 3;
NEWS = 4;
PRODUCTS = 5;
VIDEO = 6;
}
Corpus corpus = 4;
}
复制代码
Corpus枚举的第一个常量映射为零:每一个枚举定义必须包含一个映射到零的常量做为其第一个元素。这是由于:服务器
service SearchService {
rpc Search(SearchRequest)returns(SearchResponse);
}
复制代码
上面的语句就定义好了远程调用的方法名Search,待编译好对应语言的源代码以后就可使用远程调用,例如在Python中初始化SearchService方法,则执行Search方法,就是采用SearchRequest的格式去调用远程机器的方法,而后按定义好的SearchResponse格式返回调用结果。根据proto的语法定义,甚至能够实现跨平台,跨语言使用这种远程调用。 网络
根据实际工做须要,生成如下对应语言的自定义消息类型Java,Python,C ++,Go, Ruby, Objective-C,或C#的.proto文件,你须要运行protobuf 编译器protoc上.proto。若是还没有安装编译器,请下载该软件包并按照自述文件中的说明进行操做。 Protobuf 编译器的调用以下:
protoc --proto_path = IMPORT_PATH --cpp_out = DST_DIR --java_out = DST_DIR --python_out = DST_DIR --go_out = DST_DIR --ruby_out = DST_DIR --objc_out = DST_DIR --csharp_out = DST_DIR path / to / file .proto
复制代码
Python生成对应的源代码
sudo python -m pip install grpcio
python -m pip install grpcio-tools
复制代码
# 编译 proto 文件
python -m grpc_tools.protoc --python_out=. --grpc_python_out=. -I. test.proto
python -m grpc_tools.protoc: python 下的 protoc 编译器经过 python 模块(module) 实现, 因此说这一步很是省心
--python_out=. : 编译生成处理 protobuf 相关的代码的路径, 这里生成到当前目录
--grpc_python_out=. : 编译生成处理 grpc 相关的代码的路径, 这里生成到当前目录
-I. test.proto : proto 文件的路径, 这里的 proto 文件在当前目录
复制代码
编译后生成的源代码:
生成好了python能够直接实例化和调用的gRPC类,咱们就能够开始搭建RPC的服务端(远程调用提供者)和客户端(调用者)了。
from concurrent import futures
import time
import grpc
import test_pb2
import test_pb2_grpc
# 实现 proto 文件中定义的 SearchService
class RequestRpc(test_pb2_grpc.SearchService):
# 实现 proto 文件中定义的 rpc 调用
def doRequest(self, request, context):
return test_pb2.Search(query = 'hello {msg}'.format(msg = request.name)) # return的数据是符合定义的SearchResponse格式
def serve():
# 启动 rpc 服务,这里可定义最大接收和发送大小(单位M),默认只有4M
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10), options=[
('grpc.max_send_message_length', 100 * 1024 * 1024),
('grpc.max_receive_message_length', 100 * 1024 * 1024)])
test_pb2_grpc.add_SearchServiceServicer_to_server(RequestRpc(), server)
server.add_insecure_port('[::]:50051')
server.start()
try:
while True:
time.sleep(60*60*24) # one day in seconds
except KeyboardInterrupt:
server.stop(0)
if __name__ == '__main__':
serve()
复制代码
import grpc
import helloworld_pb2
import helloworld_pb2_grpc
def run():
# 链接 rpc 服务器
channel = grpc.insecure_channel('localhost:50051')
# 调用 rpc 服务
stub = test_pb2_grpc.SearchServiceStub(channel)
response = stub.doRequest(test_pb2.SearchRequest(query='henry'))
print("client received: ", response)
if __name__ == '__main__':
run()
复制代码
gRPC消息使用一种有效的二进制消息格式protobuf进行序列化。Protobuf在服务器和客户机上的序列化很是快。Protobuf序列化后的消息体积很小,可以有效负载,在移动应用程序等有限带宽场景中显得很重要。
gRPC是为HTTP/2而设计的,它是HTTP的一个主要版本,与HTTP 1.x相比具备显著的性能优点:
全部gRPC框架都为代码生成提供了一流的支持。gRPC开发的核心文件是*.proto文件 ,它定义了gRPC服务和消息的约定。根据这个文件,gRPC框架将生成服务基类,消息和完整的客户端代码。
经过在服务器和客户端之间共享*.proto文件,能够从端到端生成消息和客户端代码。客户端的代码生成消除了客户端和服务器上的重复消息,并为您建立了一个强类型的客户端。无需编写客户端代码,可在具备许多服务的应用程序中节省大量开发时间。
不存在具备JSON的HTTP API的正式规范。开发人员不须要讨论URL,HTTP动词和响应代码的最佳格式。(想一想,是用Post仍是Get好?使用Get仍是用Put好?一想到有选择恐惧症的你是否是又开了纠结,而后浪费了大量的时间)
该gRPC规范是规定有关gRPC服务必须遵循的格式。gRPC消除了争论并节省了开发人员的时间,由于gPRC在各个平台和实现之间是一致的。
HTTP/2为长期的实时通讯流提供了基础。gRPC经过HTTP/2为流媒体提供一流的支持。
gRPC服务支持全部流组合:
经过子gRPC调用截至时间和取消操做有助于实施资源使用限制。