GRPC以及python實(shí)現(xiàn)

GRPC簡(jiǎn)介

A high-performance, open-source universal RPC framework --官網(wǎng)

rpc通信

RPC(remote procedure call 遠(yuǎn)程過程調(diào)用)

  • 是什么:提供一套應(yīng)用程序之間可以通信的機(jī)制,使用C/S模型,Client進(jìn)程調(diào)用Server進(jìn)程提供的接口就像是調(diào)用本地函數(shù)一樣。
  • 為什么:相比較restful API的優(yōu)勢(shì):
    (1)gRPC都使用http底層傳輸協(xié)議建立C/S通信模型,但gRPC使用http2
    (2)gRPC可以通過protobuf來(lái)定義由嚴(yán)格約束條件的接口,在提供公共API服務(wù)時(shí),如果不希望client端給我們傳遞亂七八糟的數(shù)據(jù),就可以定義輸入的約束。
    (3)gRPC通過protobuf將數(shù)據(jù)轉(zhuǎn)化為二進(jìn)制編碼,減少傳輸數(shù)據(jù)量來(lái)提高傳輸性能。在需要服務(wù)器傳遞大量數(shù)據(jù)的場(chǎng)景下效率更高。
    (4)gRPC通過http2.0可以方便的使用streaming模式做流式通信(通常的流式數(shù)據(jù):視頻流)
  • 怎么用grpc官網(wǎng)的一個(gè)簡(jiǎn)單Python栗子
    (1)在.proto文件中定義服務(wù)。
    (2)使用協(xié)議緩沖區(qū)編譯器生成服務(wù)器和客戶端代碼。
    (3)使用Python gRPC API為您的服務(wù)編寫一個(gè)簡(jiǎn)單的客戶端和服務(wù)器。
  • 安裝
$ python -m pip install grpcio   # 安裝gprc
$ python -m pip install grpcio-tools  #安裝grpc-tools

QuitStart Python gRPC

下載代碼

$ git clone -b v1.27.0 https://github.com/grpc/grpc
$ cd grpc/examples/python/helloworld

運(yùn)行g(shù)RPC應(yīng)用:用兩個(gè)終端窗口一個(gè)運(yùn)行Server進(jìn)程,一個(gè)運(yùn)行Client進(jìn)程

$ python greeter_server.py  # 啟動(dòng)Server
$ python greeter_client.py # 在另外一個(gè)terminal啟動(dòng)client

Server端代碼

"""The Python implementation of the GRPC helloworld.Greeter server."""

from concurrent import futures
import logging

import grpc

import helloworld_pb2
import helloworld_pb2_grpc

class Greeter(helloworld_pb2_grpc.GreeterServicer):
    # 自定義對(duì)外開放的API接口
    def SayHello(self, request, context):
        return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)

def serve():
    # 使用ThreadPool并發(fā)處理Server任務(wù)
    server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
    # 把對(duì)應(yīng)的Greeter任務(wù)添加到rpc server中
    helloworld_pb2_grpc.add_GreeterServicer_to_server(Greeter(), server)
    # 這里使用的非安全接口,gRPC支持TLS/SSL安全連接,以及各種鑒權(quán)機(jī)制
    server.add_insecure_port('[::]:50051')
    server.start()
    server.wait_for_termination()

if __name__ == '__main__':
    logging.basicConfig()
    serve()

Client端代碼

"""The Python implementation of the GRPC helloworld.Greeter client."""

from __future__ import print_function
import logging

import grpc

import helloworld_pb2
import helloworld_pb2_grpc

def run():
    # NOTE(gRPC Python Team): .close() is possible on a channel and should be
    # used in circumstances in which the with statement does not fit the needs
    # of the code.
    # 使用with語(yǔ)法保證channel自動(dòng)close
    with grpc.insecure_channel('localhost:50051') as channel:
        # 客戶端通過stub來(lái)實(shí)現(xiàn)rpc通信
        stub = helloworld_pb2_grpc.GreeterStub(channel)
        # 客戶端必須使用定義好的類型,這里是HelloRequest類型
        response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
    print("Greeter client received: " + response.message)


if __name__ == '__main__':
    logging.basicConfig()
    run()

修改代碼增添自己的定制API

從proto文件中生成自己的grpc代碼

首先,拷貝examples/protos文件夾到examples/python/helloworld文件夾中并修改helloworld.proto

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
  // Sends another greeting 
  rpc SayHelloAgain (HelloRequest) returns (HelloReply) {}  // 增加一行
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

在終端執(zhí)行下面命令,重新生成helloworld_pb2.pyhelloworld_pb2_grpc.py

$ python -m grpc_tools.protoc -I./protos --python_out=. --grpc_python_out=. ./protos/helloworld.proto

可以通過vimdiff對(duì)比一下update前后代碼改動(dòng)

修改greeter_server.py增加一個(gè)Server函數(shù)SayHelloAgain

class Greeter(helloworld_pb2_grpc.GreeterServicer):
    # 自定義對(duì)外開放的API接口
    def SayHello(self, request, context):
        return helloworld_pb2.HelloReply(message='Hello, %s!' % request.name)
    def SayHelloAgain(self, request, context):     
        return helloworld_pb2.HelloReply(message='Hello again, %s!' % request.name)

修改greeter_client.py增加一個(gè)client端的調(diào)用SayHelloAgain函數(shù)的步驟

def run():
    # NOTE(gRPC Python Team): .close() is possible on a channel and should be
    # used in circumstances in which the with statement does not fit the needs
    # of the code.
    # 使用with語(yǔ)法保證channel自動(dòng)close
    with grpc.insecure_channel('localhost:50051') as channel:
        # 客戶端通過stub來(lái)實(shí)現(xiàn)rpc通信
        stub = helloworld_pb2_grpc.GreeterStub(channel)
        # 客戶端必須使用定義好的類型,這里是HelloRequest類型
        response = stub.SayHello(helloworld_pb2.HelloRequest(name='you'))
        print("Greeter client received: " + response.message)
        # new
        response = stub.SayHelloAgain(helloworld_pb2.HelloRequest(name='you'))   
        print("Greeter client received: " + response.message)

重新在不同的終端運(yùn)行Server和Client:

$ python greeter_server.py  # 啟動(dòng)Server 
$ python greeter_client.py # 在另外一個(gè)terminal啟動(dòng)client

Proto定義服務(wù)

  • 可以在examples/protos/route_guide.proto中看到完整的.proto文件
  • 定義Service
service RouteGuide {
   // (Method definitions not shown)
}
  • 然后,在服務(wù)定義中定義rpc方法,并指定它們的請(qǐng)求和響應(yīng)類型。gRPC可以定義四種服務(wù)方法,所有這些方法都在RouteGuide服務(wù)中看到:
  • (1)simple RPC :一種簡(jiǎn)單的RPC,客戶端使用Stub將請(qǐng)求發(fā)送到服務(wù)器,然后等待響應(yīng)返回,就像普通的函數(shù)調(diào)用一樣。
// Obtains the feature at a given position.
rpc GetFeature(Point) returns (Feature) {}
  • (2)response-streaming RPC:響應(yīng)流式RPC,客戶端向服務(wù)器發(fā)送請(qǐng)求,并獲取 流 以讀取回一系列消息。客戶端從返回的流中讀取,直到?jīng)]有更多消息為止。如示例所示,您可以通過將stream關(guān)鍵字放在響應(yīng)類型之前來(lái)指定響應(yīng)流方法。
// Obtains the Features available within the given Rectangle.  Results are 
// streamed rather than returned at once (e.g. in a response message with a 
// repeated field), as the rectangle may cover a large area and contain a 
// huge number of features.
rpc ListFeatures(Rectangle) returns (stream Feature) {}
  • (3)request-streaming RPC :請(qǐng)求流式RPC,客戶端編寫消息序列,然后再次使用提供的流將消息發(fā)送到服務(wù)器。客戶端寫完消息后,它將等待服務(wù)器讀取所有消息并返回其響應(yīng)。您可以通過將stream關(guān)鍵字放在請(qǐng)求類型之前來(lái)指定請(qǐng)求流方法。
// Accepts a stream of Points on a route being traversed, returning a
// RouteSummary when traversal is completed.
rpc RecordRoute(stream Point) returns (RouteSummary) {}

*(4)bidirectionally-streaming RPC:雙向流式RPC,雙方都使用讀寫流發(fā)送一系列消息。這兩個(gè)流是獨(dú)立運(yùn)行的,因此客戶端和服務(wù)器可以按照自己喜歡的順序進(jìn)行讀寫:例如,服務(wù)器可以在寫響應(yīng)之前等待接收所有客戶端消息,或者可以先讀取一條消息再寫入一條消息,或其他一些讀寫組合。每個(gè)流中的消息順序都會(huì)保留。您可以通過在請(qǐng)求和響應(yīng)之前都放置stream關(guān)鍵字來(lái)指定這種類型的方法。

// Accepts a stream of RouteNotes sent while a route is being traversed, 
// while receiving other RouteNotes (e.g. from other users).
rpc RouteChat(stream RouteNote) returns (stream RouteNote) {}
  • .proto文件還包含我們服務(wù)方法中使用的所有請(qǐng)求和響應(yīng)類型的協(xié)議緩沖區(qū)消息類型定義-例如,這是Point消息類型:
// Points are represented as latitude-longitude pairs in the E7 representation
// (degrees multiplied by 10**7 and rounded to the nearest integer).
// Latitudes should be in the range +/- 90 degrees and longitude should be in
// the range +/- 180 degrees (inclusive).
message Point {
  int32 latitude = 1;
  int32 longitude = 2;
}
  • 舉個(gè)栗子:route_guide_server.py里的class RouteGuideServicer且數(shù)據(jù)庫(kù)里的數(shù)據(jù)如下:每個(gè)地點(diǎn)都有name和location,其中l(wèi)ocation里包含經(jīng)度和緯度:
{
    "location": {
        "latitude": 407838351,
        "longitude": -746143763
    },
    "name": "Patriots Path, Mendham, NJ 07945, USA"
}
  • (1)simple RPC:簡(jiǎn)單的RPC。
# request:一個(gè)location的經(jīng)度和緯度
# response:是location,包含name
def GetFeature(self, request, context):
  feature = get_feature(self.db, request)
  if feature is None:
    return route_guide_pb2.Feature(name="", location=request)
  else:
    return feature
  • (2)response-streaming RPC:響應(yīng)流式RPC。
# request:一個(gè)地區(qū)的四個(gè)邊的經(jīng)度緯度
# response:落在這個(gè)地區(qū)的所有點(diǎn),按照yield的方式挨個(gè)返回,流式返回
def ListFeatures(self, request, context):
  left = min(request.lo.longitude, request.hi.longitude)
  right = max(request.lo.longitude, request.hi.longitude)
  top = max(request.lo.latitude, request.hi.latitude)
  bottom = min(request.lo.latitude, request.hi.latitude)
  for feature in self.db:
    if (feature.location.longitude >= left and
        feature.location.longitude <= right and
        feature.location.latitude >= bottom and
        feature.location.latitude <= top):
      yield feature
  • (3)request-streaming RPC:請(qǐng)求流式RPC。
# request:是一個(gè)迭代器iterator,包含一系列l(wèi)ocation
# response: 篩選所有在數(shù)據(jù)庫(kù)里的點(diǎn),返回點(diǎn)的個(gè)數(shù),數(shù)據(jù)庫(kù)內(nèi)點(diǎn)的個(gè)數(shù),按順序走完所有l(wèi)ocation的距離長(zhǎng)度
def RecordRoute(self, request_iterator, context):
  point_count = 0
  feature_count = 0
  distance = 0.0
  prev_point = None

  start_time = time.time()
  for point in request_iterator:
    point_count += 1
    if get_feature(self.db, point):
      feature_count += 1
    if prev_point:
      distance += get_distance(prev_point, point)
    prev_point = point

  elapsed_time = time.time() - start_time
  return route_guide_pb2.RouteSummary(point_count=point_count,
                                      feature_count=feature_count,
                                      distance=int(distance),
                                      elapsed_time=int(elapsed_time))
  • (4)bidirectionally-streaming RPC:雙向流式RPC。
# request: 是一個(gè)迭代器iterator,包含一系列l(wèi)ocation
# response: 根據(jù)每個(gè)location,找到location集里一樣的點(diǎn)
def RouteChat(self, request_iterator, context):
  prev_notes = []
  for new_note in request_iterator:
    for prev_note in prev_notes:
      if prev_note.location == new_note.location:
        yield prev_note
    prev_notes.append(new_note)

啟動(dòng)Server

完成了RouteGuide的所有API,就可以啟動(dòng)Server了,另外start()不會(huì)阻塞,所以如果在服務(wù)期間代碼沒有其他事情要做,server則可能需要進(jìn)入睡眠循環(huán)。

def serve():
  server = grpc.server(futures.ThreadPoolExecutor(max_workers=10))
  route_guide_pb2_grpc.add_RouteGuideServicer_to_server(
      RouteGuideServicer(), server)
  server.add_insecure_port('[::]:50051')
  server.start()

創(chuàng)建Client

參考完整的Client代碼:
examples/python/route_guide/route_guide_client.py.

  • 首先,創(chuàng)建一個(gè)Stub
channel = grpc.insecure_channel('localhost:50051') 
stub = route_guide_pb2_grpc.RouteGuideStub(channel)
  • 然后,Call service methods
    對(duì)于返回單個(gè)響應(yīng)的RPC方法(“response-unary”方法),gRPC Python支持同步(阻塞)和異步(非阻塞)控制流語(yǔ)義。對(duì)于響應(yīng)流式RPC方法,調(diào)用立即返回響應(yīng)值的迭代器。調(diào)用該迭代器的next()方法塊,直到要從該迭代器產(chǎn)生的響應(yīng)變?yōu)榭捎脼橹埂?/li>
  • (1)Simple RPC:簡(jiǎn)單RPC。GetFeature的同步調(diào)用幾乎與調(diào)用本地方法一樣簡(jiǎn)單。RPC調(diào)用等待服務(wù)器響應(yīng),并且將返回響應(yīng)或引發(fā)異常;GetFeature的異步調(diào)用與此類似,但是就像在線程池中異步調(diào)用本地方法一樣:
# 同步調(diào)用
feature = stub.GetFeature(point)
# 異步調(diào)用
feature_future = stub.GetFeature.future(point) 
feature = feature_future.result()
  • (2)Response-streaming RPC:響應(yīng)流RPC。調(diào)用響應(yīng)流ListFeatures類似于使用序列類型:
for feature in stub.ListFeatures(rectangle):
  • (3)Request-streaming RPC:請(qǐng)求流式RPC。調(diào)用請(qǐng)求流式RecordRoute類似于將迭代器傳遞給本地方法。就像上面的簡(jiǎn)單RPC也返回單個(gè)響應(yīng)一樣,可以同步或異步調(diào)用它:
# 同步
route_summary = stub.RecordRoute(point_iterator)
# 異步
route_summary_future = stub.RecordRoute.future(point_iterator)
route_summary = route_summary_future.result()
  • (4)Bidirectional streaming RPC:雙向流RPC。調(diào)用雙向流RouteChat具有服務(wù)流和響應(yīng)流語(yǔ)義的組合(在服務(wù)端就是這種情況):
for received_route_note in stub.RouteChat(sent_route_note_iterator):

參考:

gRPC 官網(wǎng)
gRPC python QuickStart
gRPC python basic tutorial

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 228,119評(píng)論 6 531
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 98,382評(píng)論 3 415
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,038評(píng)論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,853評(píng)論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 71,616評(píng)論 6 408
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 55,112評(píng)論 1 323
  • 那天,我揣著相機(jī)與錄音,去河邊找鬼。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,192評(píng)論 3 441
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 42,355評(píng)論 0 288
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 48,869評(píng)論 1 334
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 40,727評(píng)論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 42,928評(píng)論 1 369
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,467評(píng)論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 44,165評(píng)論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,570評(píng)論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,813評(píng)論 1 282
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 51,585評(píng)論 3 390
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 47,892評(píng)論 2 372

推薦閱讀更多精彩內(nèi)容

  • gRPC 是一個(gè)高性能、通用的開源RPC框架,基于HTTP/2協(xié)議標(biāo)準(zhǔn)和Protobuf序列化協(xié)議開發(fā),支持眾多的...
    小波同學(xué)閱讀 19,516評(píng)論 6 19
  • 1.簡(jiǎn)介 在gRPC中,客戶端應(yīng)用程序可以直接調(diào)用不同計(jì)算機(jī)上的服務(wù)器應(yīng)用程序上的方法,就像它是本地對(duì)象一樣,使您...
    第八共同體閱讀 1,915評(píng)論 0 6
  • 該篇文章介紹了golang的grpc編程。 通過下面的例子,你將會(huì)學(xué)到:1. 在一個(gè).proto文件里define...
    曉_7611閱讀 1,392評(píng)論 0 3
  • 原文出處:gRPC gRPC分享 概述 gRPC 一開始由 google 開發(fā),是一款語(yǔ)言中立、平臺(tái)中立、開源的遠(yuǎn)...
    小波同學(xué)閱讀 7,268評(píng)論 0 18
  • 最近項(xiàng)目需要用到 gRPC,網(wǎng)上gRPC 的資料較少,翻譯了官網(wǎng)的 gRPC guide 文檔,以供組內(nèi)學(xué)習(xí),部分...
    不智魚閱讀 6,110評(píng)論 0 13