關於golang寫grpc 服務記錄

總結一下用grpc協議寫服務的若干心得,並記錄一些有用鏈接。

  1. 下載grpc依賴及安裝grpc代碼生成工具, 也可參見該鏈接創建簡單可行的helloworld服務。

  2. golang 開發grpc服務用"google.golang.org/grpc", 參考此例子, 及閱讀官方的tutorial可以應付大部分開發情況。

  3. grpc服務端及客戶端均需要保留protoc生成的.pb.go文件作爲交互依據。

  4. .proto文件指定了grpc服務端和客戶端交互的序列化規定,即指定了客戶端如何將請求內容序列化成字節, 服務端收到請求字節後也根據.proto文件的內容反序列化成對象。 理解上跟在go結構體屬性的json:"user_name"類似,只是proto的序列化和反序列化效率更高

  5. 使用protoc生成的.pb.go文件也會生成json",,,"db:",,,"等tag, 方便開發者在restful服務和grpc服務自由轉換和選擇, protoc本身不支持指定json:... tag的命名, 只能通過輔助插件在生成.pb.go時或事後修改.pb.go的tag屬性。

  6. 若在原restful web服務中開發grpc服務, 可將原指定數據交互的數據結構文件如model.go/types.go.pb.go合爲一份。但這也會引起一系列問題。如生產的pb.go中的結構體不支持embedded, 以及不能指定生成的pb.go中的結構體去使用本身以經用golang定義好的結構體。

  7. Restful web服務開發和grpc服務交互也會引起很多序列化麻煩,儘量在開發前設計好序列化流程, 可將json,protobuf, db等三者的序列化由同一個model定義, 省去互相轉換的麻煩。

type User struct{
Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty" db:"name"`
}
  1. protoc 有protoc 2 和protoc 3, 也有不少類型的支持, 可通過導入類型依賴包來使用更多的數據類型

  2. grpc可以實現連接池。在實踐中發現,單個*grpc.ClientConn支持多線程併發使用,瓶頸不在於*grpc.ClientConn數量,參見此文及其issuegrpc連接池的建立會佔用較多資源, 即每個*grpc.ClientConn均有若干goroutine作爲watch協程, 一旦連接池的量級較大時, 佔用的資源也可觀的。

  3. grpc瞬時流量大會引起 grpc Resource Exhausted , 可通過配置grpc服務端和客戶端來解決參見

  4. grpc不能用於前端瀏覽器與後端直接交互, 其grpc 的典型應用場景如下:
    在這裏插入圖片描述

  5. 使用場景如Kafka接入使用,往往只需使用grpc的序列化結構體, 而不使用grpc的方法,以優化序列化和反序列化的性能。

proto.Unmarshal(......)
  1. 進行grpc 方法調用需要注意內存泄露, 這裏引用源碼上的註釋作爲解釋:
// NewStream creates a new Stream for the client side. This is typically
// called by generated code. ctx is used for the lifetime of the stream.
//
// To ensure resources are not leaked due to the stream returned, one of the following
// actions must be performed:
//
//      1. Call Close on the ClientConn.
//      2. Cancel the context provided.
//      3. Call RecvMsg until a non-nil error is returned. A protobuf-generated
//         client-streaming RPC, for instance, might use the helper function
//         CloseAndRecv (note that CloseSend does not Recv, therefore is not
//         guaranteed to release all resources).
//      4. Receive a non-nil, non-io.EOF error from Header or SendMsg.
//
// If none of the above happen, a goroutine and a context will be leaked, and grpc
// will not call the optionally-configured stats handler with a stats.End message.
func (cc *ClientConn) NewStream(ctx context.Context, desc *StreamDesc, method string, opts ...CallOption) (ClientStream, error) {...}

對於http handler調用grpc方法的情況, 爲防止內存泄露,可以爲各handler增加利用context控制超時的中間件

  1. 幾個grpc的典型應用舉例

14 gRPC stream 允許·grpc·方法調用的時候建立長連接, 允許客戶端和服務端建立雙向連接,具體詳見。後臺服務通過stream連接可實現更豐富的交互形式。如服務A基於etcd建立元數據服務, 服務B可通過stream 達到訂閱watch特定元數據的效果, 確保獲取最新的元數據改動。

15 後臺開發中涉及到較多的.proto文件時, 可以建立獨立項目.proto文件統一保存, 通過git管理。項目中也可以將.proto文件構建成對應編程語言的文件, 如foo.pb.go, 讓有需要的項目直接通過import來導入。對於沒有上傳到github的項目, 可以通過replace方式來實現本地import詳見

16 後臺服務中, 若涉及到多個功能模塊的grpc方法, 則建議每個功能模塊設置獨立的grpc server ,如kebelet的grpc server源碼, 若服務中有多個grpc server 則需要監聽不同的tcp port如kebelet實際上運行着多個grpc server。

17. 實踐中發現,數據結構越是複雜的情況, 越不適合將proto生成的xxx.pb.go中的結構體作爲通用的model來提供給restfulgrpc接口使用,這是由於生成的xxx.pb.go有若干點侷限:

  • 不支持embeded結構, 由此生成的struct本身難以沿用embeded屬性的特性, issue#192
  • 生成的struct若要實現某interface,需要在xxx.pb.gopackage下另起文件實現。
  • 生成的structslice定義只能爲[]*object

在大部分情況下,restfulgrpc接口對model部分的關注點會有差異, 因此實現restful modelgrpc model的轉換會效率更高, 例子詳見

  1. grpcrpc的原理類似(區別見此), 大致爲開發者定義protobuf的數據結構和方法接口, 編寫方法實現並實現該接口。服務啓動時rpc server端將protobuf指定的接口註冊到內存(map結構), 然後監聽指定的socketclient端調用rpc方法時,序列化調用參數, 向server端指定端口發起tcp請求及發送參數數據, server端查詢內存找到client端請求的方法對應的實現,運行該方法並反序列化,返回結果。 另外rpc基於tcp/udpgrpc基於http
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章