gRPC概述
gRPC是一款高性能、開源的 RPC 框架,產自 Google,基於 ProtoBuf 序列化協議進行開發,支持多種語言(Golang、Python、Java等)。gRPC對HTTP2.0協議的支持使其在 Android、IOS 等客戶端後端服務的開發領域具有良好的前景。gRPC 提供了一種簡單的方法來定義服務,同時客戶端可以充分利用HTTP2.0 stream的特性,從而有助於節省帶寬、降低 TCP 的連接次數、節省CPU的使用等。所謂RPC(remote procedure call 遠程過程調用)框架實際是提供了一套機制,使得應用程序之間可以進行通信,而且也遵從server/client模型。使用的時候客戶端調用server端提供的接口就像是調用本地的函數一樣。
gRPC優勢
gRPC和Restful API都提供了一套通信機制,用於server/client模型通信,而且它們都使用http作爲底層的傳輸協議。不過gRPC有些特有的優勢如下:
- gRPC可以通過protobuf來定義接口,從而可以有更加嚴格的接口約束條件。
- 通過protobuf可以將數據序列化爲二進制編碼,這會大幅減少需要傳輸的數據量,從而大幅提高性能。
- gRPC可以方便地支持流式通信。(理論上通過http2.0就可以使用streaming模式, 但是通常web服務的Restful API似乎很少這麼用,通常的流式數據應用如視頻流,一般都會使用專門的協議如HLS,RTMP等,這些就不是我們通常web服務了,而是有專門的服務器應用)
gRPC場景
- 需要對接口進行嚴格約束的情況。比如我們提供一個公共的服務,很多客戶端可以訪問這個服務,這時對於接口我們希望有更加嚴格的約束,我們不希望客戶端給我們傳遞任意的數據,尤其是考慮到安全性的因素,我們通常需要對接口進行更加嚴格的約束。這時gRPC就可以通過protobuf來提供嚴格的接口約束。
- 對於性能有更高的要求的情況。有時我們的服務需要傳遞大量的數據,而又希望不影響我們的性能,這個時候也可以考慮gRPC服務,因爲通過protobuf我們可以將數據壓縮編碼轉化爲二進制格式,通常傳遞的數據量要小得多,而且通過http2我們可以實現異步的請求,從而大大提高了通信效率。
gRPC安裝
在線:
pip install grpcio
pip install protobuf
pip install grpcio_tools
離線
https://pypi.org/project/grpcio/#files
https://pypi.org/project/protobuf/#files
https://pypi.org/project/grpcio-tools/#files
gRPC示例
1、protobuf定義接口和數據類型
syntax = "proto3";
package firstmsg;
service FirstMsg {
rpc firstMsg(FirstMsgRequest) returns (FirstMsgResponse) {}
}
message FirstMsgRequest {
string body = 1;
}
message FirstMsgResponse {
string content = 1;
}
python -m grpc_tools.protoc -I. --python_out=. --grpc_python_out=. first_msg.proto
-I 指定協議文件的查找目錄 設置爲當前目錄./
--python_out 指定 xxxxxx_pb2.py的輸出路徑
--grpc_python_out 指定xxxxxx_pb2_grpc.py文件的輸出路徑
2、gRPC Server 端代碼
# -*- coding:utf-8 -*-
import sys
import json
import time
from concurrent import futures
from mgrpc.proto.first_msg_pb2 import *
from mgrpc.proto.first_msg_pb2_grpc import *
default_encoding = "utf-8"
if sys.getdefaultencoding() != default_encoding:
reload(sys)
sys.setdefaultencoding(default_encoding)
class CFirstMsgServicer(FirstMsgServicer):
def firstMsg(self, request, context):
request_body = request.body
print 'receive client request : {}'.format(request_body)
if request_body:
data = json.loads(request_body)
print 'parse client request : {}'.format(data)
return FirstMsgResponse(content=json.dumps({"code": 200, "data": "success"}))
else:
return FirstMsgResponse(content=json.dumps({"code": 500, "data": "failure"}))
def startup():
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
add_FirstMsgServicer_to_server(CFirstMsgServicer(), server)
server.add_insecure_port('127.0.0.1:10080')
server.start()
try:
while True:
time.sleep(3600)
except KeyboardInterrupt:
server.stop(0)
if __name__ == '__main__':
startup()
3、gRPC Client 端代碼
# -*- coding:utf-8 -*-
import sys
import json
from datetime import datetime
from mgrpc.proto.first_msg_pb2 import *
from mgrpc.proto.first_msg_pb2_grpc import *
default_encoding = 'utf-8'
if sys.getdefaultencoding() != default_encoding:
reload(sys)
sys.setdefaultencoding(default_encoding)
def invoke_first_msg_service():
channel = grpc.insecure_channel('127.0.0.1:10080')
first_msg_stub = FirstMsgStub(channel)
msg = {"from": 1, "to": 2, "op_time": datetime.now().strftime('%Y-%m-%d %H:%M:%S'), "msg": "hello"}
response = first_msg_stub.firstMsg(FirstMsgRequest(body=json.dumps(msg)))
print 'response content : {}'.format(response.content)
response = first_msg_stub.firstMsg(FirstMsgRequest(body=None))
print 'response content : {}'.format(response.content)
if __name__ == '__main__':
invoke_first_msg_service()