gRPC是什么腕窥?
gRPC是什么可以用官網(wǎng)的一句話來概括
A high-performance, open-source universal RPC framework
所謂RPC(remote procedure call 遠程過程調(diào)用)框架實際是提供了一套機制闸英,使得應(yīng)用程序之間可以進行通信,而且也遵從server/client模型慈俯。使用的時候客戶端調(diào)用server端提供的接口就像是調(diào)用本地的函數(shù)一樣咱枉。如下圖所示就是一個典型的RPC結(jié)構(gòu)圖。
gRPC有什么好處以及在什么場景下需要用gRPC
既然是server/client模型觉增,那么我們直接用restful api不是也可以滿足嗎彻亲,為什么還需要RPC呢孕锄?下面我們就來看看RPC到底有哪些優(yōu)勢
gRPC vs. Restful API
gRPC和restful API都提供了一套通信機制吮廉,用于server/client模型通信,而且它們都使用http作為底層的傳輸協(xié)議(嚴(yán)格地說, gRPC使用的http2.0畸肆,而restful api則不一定)宦芦。不過gRPC還是有些特有的優(yōu)勢,如下:
- gRPC可以通過protobuf來定義接口轴脐,從而可以有更加嚴(yán)格的接口約束條件调卑。關(guān)于protobuf可以參見筆者之前的小文Google Protobuf簡明教程
- 另外,通過protobuf可以將數(shù)據(jù)序列化為二進制編碼大咱,這會大幅減少需要傳輸?shù)臄?shù)據(jù)量令野,從而大幅提高性能。
- gRPC可以方便地支持流式通信(理論上通過http2.0就可以使用streaming模式, 但是通常web服務(wù)的restful api似乎很少這么用徽级,通常的流式數(shù)據(jù)應(yīng)用如視頻流,一般都會使用專門的協(xié)議如HLS聊浅,RTMP等餐抢,這些就不是我們通常web服務(wù)了,而是有專門的服務(wù)器應(yīng)用低匙。)
使用場景
- 需要對接口進行嚴(yán)格約束的情況旷痕,比如我們提供了一個公共的服務(wù),很多人顽冶,甚至公司外部的人也可以訪問這個服務(wù)欺抗,這時對于接口我們希望有更加嚴(yán)格的約束,我們不希望客戶端給我們傳遞任意的數(shù)據(jù)强重,尤其是考慮到安全性的因素绞呈,我們通常需要對接口進行更加嚴(yán)格的約束。這時gRPC就可以通過protobuf來提供嚴(yán)格的接口約束间景。
- 對于性能有更高的要求時佃声。有時我們的服務(wù)需要傳遞大量的數(shù)據(jù),而又希望不影響我們的性能倘要,這個時候也可以考慮gRPC服務(wù)圾亏,因為通過protobuf我們可以將數(shù)據(jù)壓縮編碼轉(zhuǎn)化為二進制格式,通常傳遞的數(shù)據(jù)量要小得多封拧,而且通過http2我們可以實現(xiàn)異步的請求志鹃,從而大大提高了通信效率。
但是泽西,通常我們不會去單獨使用gRPC曹铃,而是將gRPC作為一個部件進行使用,這是因為在生產(chǎn)環(huán)境尝苇,我們面對大并發(fā)的情況下铛只,需要使用分布式系統(tǒng)來去處理埠胖,而gRPC并沒有提供分布式系統(tǒng)相關(guān)的一些必要組件。而且淳玩,真正的線上服務(wù)還需要提供包括負載均衡直撤,限流熔斷,監(jiān)控報警蜕着,服務(wù)注冊和發(fā)現(xiàn)等等必要的組件谋竖。不過,這就不屬于本篇文章討論的主題了承匣,我們還是先繼續(xù)看下如何使用gRPC蓖乘。
gRPC HelloWorld實例詳解
gRPC的使用通常包括如下幾個步驟:
- 通過protobuf來定義接口和數(shù)據(jù)類型
- 編寫gRPC server端代碼
-
編寫gRPC client端代碼
下面來通過一個實例來詳細講解上述的三步。
下邊的hello world實例完成之后韧骗,其目錄結(jié)果如下:
定義接口和數(shù)據(jù)類型
- 通過protobuf定義接口和數(shù)據(jù)類型
syntax = "proto3";
package rpc_package;
// define a service
service HelloWorldService {
// define the interface and data type
rpc SayHello (HelloRequest) returns (HelloReply) {}
}
// define the data type of request
message HelloRequest {
string name = 1;
}
// define the data type of response
message HelloReply {
string message = 1;
}
- 使用gRPC protobuf生成工具生成對應(yīng)語言的庫函數(shù)
python -m grpc_tools.protoc -I=./protos --python_out=./rpc_package --grpc_python_out=./rpc_package ./protos/user_info.proto
這個指令會自動生成rpc_package文件夾中的helloworld_pb2.py
和helloworld_pb2_grpc.py
嘉抒,但是不會自動生成__init__.py
文件,需要我們手動添加
關(guān)于protobuf的詳細解釋請參考Google Protobuf簡明教程
gRPC server端代碼
#!/usr/bin/env python
# -*-coding: utf-8 -*-
from concurrent import futures
import grpc
import logging
import time
from rpc_package.helloworld_pb2_grpc import add_HelloWorldServiceServicer_to_server, \
HelloWorldServiceServicer
from rpc_package.helloworld_pb2 import HelloRequest, HelloReply
class Hello(HelloWorldServiceServicer):
# 這里實現(xiàn)我們定義的接口
def SayHello(self, request, context):
return HelloReply(message='Hello, %s!' % request.name)
def serve():
# 這里通過thread pool來并發(fā)處理server的任務(wù)
server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
# 將對應(yīng)的任務(wù)處理函數(shù)添加到rpc server中
add_HelloWorldServiceServicer_to_server(Hello(), server)
# 這里使用的非安全接口袍暴,世界gRPC支持TLS/SSL安全連接些侍,以及各種鑒權(quán)機制
server.add_insecure_port('[::]:50000')
server.start()
try:
while True:
time.sleep(60 * 60 * 24)
except KeyboardInterrupt:
server.stop(0)
if __name__ == "__main__":
logging.basicConfig()
serve()
gRPC client端代碼
#!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function
import logging
import grpc
from rpc_package.helloworld_pb2 import HelloRequest, HelloReply
from rpc_package.helloworld_pb2_grpc import HelloWorldServiceStub
def run():
# 使用with語法保證channel自動close
with grpc.insecure_channel('localhost:50000') as channel:
# 客戶端通過stub來實現(xiàn)rpc通信
stub = HelloWorldServiceStub(channel)
# 客戶端必須使用定義好的類型,這里是HelloRequest類型
response = stub.SayHello(HelloRequest(name='eric'))
print ("hello client received: " + response.message)
if __name__ == "__main__":
logging.basicConfig()
run()
演示
先執(zhí)行server端代碼
python hello_server.py
接著執(zhí)行client端代碼如下:
? grpc_test python hello_client.py
hello client received: Hello, eric!