編寫一個go gRPC的服務

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。

前置條件:

擷取 gRPC-go 源碼

$ go get google.golang.org/grpc

簡單例子的源碼位置:

$ cd $GOPATH/src/google.golang.org/grpc/examples/helloworld

複雜些例子的源碼位置:

$ cd $GOPATH/src/google.golang.org/grpc/examples/route_guide

寫一個gRPC的服務,一般分下面幾步:

  • 在一個 .proto 檔案內定義服務。
  • 用 protocol buffer 編譯器產生伺服器和用戶端代碼。
  • 使用 gRPC 的 Go API 為你的服務實現一個簡單的用戶端和伺服器。

定義服務

在一個 .proto 檔案中定義服務

簡單的例子服務定義在: examples/helloworld/helloworld/helloworld.proto

route_guide 的定義在 : examples/route_guide/routeguide/route_guide.proto

要定義一個服務,你必須在你的 .proto 檔案中指定 service:

然後在你的服務中定義 rpc 方法,指定請求的和響應類型。

gRPC 允許你定義4種類型的 service 方法,這些都在 RouteGuide 服務中使用到了:

簡單RPC

一個 簡單 RPC , 用戶端發送帶參請求到伺服器並等待響應返回,就像平常的函數調用一樣。

伺服器端流式 RPC

一個 伺服器端流式 RPC , 用戶端發送請求到伺服器,拿到一個流去讀取返回的訊息序列。 用戶端讀取返回的流,直到裡面沒有任何訊息。從例子中可以看出,通過在 響應 類型前插入 stream 關鍵字,可以指定一個伺服器端的流方法。

用戶端流式 RPC

一個 用戶端流式 RPC , 用戶端寫入一個訊息序列並將其發送到伺服器,同樣也是使用流。一旦用戶端完成寫入訊息,它等待伺服器完成讀取返回它的響應。通過在 請求 類型前指定 stream 關鍵字來指定一個用戶端的流方法。

雙向流式 RPC

一個 雙向流式 RPC 是雙方使用讀寫流去發送一個訊息序列。兩個流獨立操作,因此用戶端和伺服器可以以任意喜歡的順序讀寫:比如, 伺服器可以在寫入響應前等待接收所有的用戶端訊息,或者可以交替的讀取和寫入訊息,或者其他讀寫的組合。 每個流中的訊息順序被預留。你可以通過在請求和響應前加 stream 關鍵字去制定方法的類型。

訊息類型

上面看起來像函數參數、傳回值得,由於要涉及到跨伺服器調用,這些其實傳遞的是訊息。我們的 .proto 檔案也包含了所有請求的 protocol buffer 訊息類型定義以及在服務方法中使用的響應類型——比如,下面的Point訊息類型:

 

產生伺服器和用戶端代碼

我們需要通過 protocol buffer 的編譯器 protoc 以及一個特殊的 gRPC Go 外掛程式來完成用 protocol buffer 編譯器產生伺服器和用戶端代碼。

 

簡單期間,有個 bash 指令碼可以幫我們產生合適的代碼  codegen.sh  (https://github.com/grpc/grpc-go/blob/master/codegen.sh)

 

運行 codegen.sh route_guide.proto 就可以在目前的目錄下產生 route_guide.pb.go 檔案。

在這個產生的檔案中, 既有 routeGuideClient 用戶端代碼部分, 也有 routeGuideRouteChatServer 伺服器段代碼部分。

建立伺服器

這部分的源碼在: grpc-go/examples/route_guide/server/server.go

讓 RouteGuide 服務工作有兩個部分:

  • 實現我們服務定義的產生的服務介面:做我們的服務的實際的“工作”。
  • 運行一個 gRPC 伺服器,監聽來自用戶端的請求並返回服務的響應。

 

實現RouteGuide

在源碼中,我們可以看到實現了介面RouteGuideServer的routeGuideServer資料結構。  這個介面是在route_guide.pb.go中自動產生的。

簡單RPC

GetFeature,它從用戶端拿到一個 Point 對象,然後從返回包含從資料庫拿到的feature資訊的 Feature.


該方法傳入了 RPC 的內容物件,以及用戶端的 Point 參數。它返回了Feature 響應資訊和error資訊。

在方法中我們遍曆所有伺服器端儲存的資訊,找到位置資訊匹配的,然後將其和一個nil錯誤一起返回給用戶端。

 

伺服器端流式 RPC

ListFeatures 是一個伺服器端的流式 RPC,我們將多個 Feature 發回給用戶端。

這裡的請求參數是一個 Rectangle,用戶端期望返回多個 Feature,這次我們使用了一個請求對象和一個特殊的RouteGuide_ListFeaturesServer來寫入我們的響應,而不是得到方法參數中的入參和傳回值。
在這個方法中,我們填充了儘可能多的 Feature 對象去返回,用steam的 Send() 方法把它們寫入 RouteGuide_ListFeaturesServer。

最後,我們返回了一個 nil 錯誤告訴 gRPC 響應的寫入已經完成。

如果在調用過程中發生任何錯誤,我們會返回一個非 nil 的錯誤;

 

用戶端流式 RPC

用戶端流方法 RecordRoute,我們通過它可以從用戶端拿到一個 Point 的流,其中包括我們需要的Point資訊。

這次這個方法用了一個 RouteGuide_RecordRouteServer 流,伺服器可以用它來同時讀 和 寫訊息——它可以用自己的 Recv() 方法接收用戶端訊息並且用 SendAndClose() 方法返回它的單個響應。

在方法體中,我們使用 RouteGuide_RecordRouteServer 的 Recv() 方法去反覆讀取用戶端的請求到一個請求對象(在這個情境下是 Point),直到沒有更多的訊息。

伺服器需要在每次調用後檢查 Read() 返回的錯誤。如果傳回值為 nil,流依然完好,可以繼續讀取;

如果傳回值為 io.EOF,訊息流程結束,伺服器可以返回它的 RouteSummary。

如果它還有其它值,我們原樣返回錯誤,gRPC 層會把它轉換為 RPC 狀態。

 

雙向流式 RPC

雙向流式 RPC RouteChat()。

這裡讀寫的文法和用戶端流方法相似,除了伺服器會使用流的 Send() 方法而不是 SendAndClose(),因為它需要寫多個響應。雖然用戶端和伺服器端總是會拿到對方寫入時順序的訊息,它們可以以任意順序讀寫——流的操作是完全獨立的。

 

啟動伺服器

一旦我們實現了所有的方法,我們還需要啟動一個gRPC伺服器,這樣用戶端才可以使用服務。

為了構建和啟動伺服器,我們需要:

  • 使用 lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port)) 指定我們期望用戶端請求的監聽連接埠。
  • 使用grpc.NewServer()建立 gRPC 伺服器的一個執行個體。
  • 在 gRPC 伺服器註冊我們的服務實現。
  • 用伺服器 Serve() 方法以及我們的連接埠資訊區實現阻塞等待,直到進程被殺死或者 Stop() 被調用。

 

建立用戶端

建立跟伺服器的串連

為了調用服務方法,我們首先建立一個 gRPC conn。我們通過給 grpc.Dial() 傳入伺服器位址和連接埠號碼做到這點,如下:

你可以使用 DialOptions 在 grpc.Dial 中設定授權認證(如, TLS,GCE認證,JWT認證),如果服務有這樣的要求的話 —— 但是對於 RouteGuide 服務,我們不用這麼做。

一旦 gRPC conn 建立起來,我們需要一個client去執行 RPC。我們通過 .proto 產生的 pb 包提供的 NewRouteGuideClient 方法來完成。

調用伺服器方法

簡單RPC

調用簡單 RPC GetFeature 幾乎是和調用一個本地方法一樣直觀。

伺服器端流式 RPC

我們給方法傳入一個上下文和請求。然而,我們得到返回的是一個 RouteGuide_ListFeaturesClient 執行個體,而不是一個應答對象。用戶端可以使用 RouteGuide_ListFeaturesClient 流去讀取伺服器的響應。

我們使用 RouteGuide_ListFeaturesClientRecv() 方法去反覆讀取伺服器的響應到一個響應 protocol buffer 對象(在這個情境下是Feature)直到訊息讀取完畢:每次調用完成時,用戶端都要檢查從 Recv() 返回的錯誤 err。如果返回為 nil,流依然完好並且可以繼續讀取;如果返回為 io.EOF,則說明訊息流程已經結束;否則就一定是一個通過 err 傳過來的 RPC 錯誤。

用戶端流式 RPC

RouteGuide_RecordRouteClient 有一個 Send() 方法,我們可以用它來給伺服器發送請求。一旦我們完成使用 Send() 方法將用戶端請求寫入流,就需要調用流的 CloseAndRecv()方法,讓 gRPC 知道我們已經完成了寫入同時期待返回應答。我們從 CloseAndRecv() 返回的 err 中獲得 RPC 的狀態。如果狀態為nil,那麼CloseAndRecv()的第一個傳回值將會是合法的伺服器應答。


雙向流式 RPC

我們只給函數傳入一個內容物件,拿到可以用來讀寫的流。

運行例子

伺服器端:

$ go run server/server.go

用戶端:

$ go run client/client.go

 

參考資料:

中文 gRPC 基礎 Go   http://doc.oschina.net/grpc?t=60133 

英文 gRPC 基礎 Go  http://www.grpc.io/docs/tutorials/basic/go.html

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.