什麼是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調用截至時間和取消操做有助於實施資源使用限制。