日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

gRPC入门

發布時間:2025/3/21 编程问答 33 豆豆
生活随笔 收集整理的這篇文章主要介紹了 gRPC入门 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
這篇文章是給別人寫的,不是我要寫的。我也來賣個萌!!

—— 時間飛逝 如一名攜帶信息的郵差 但那只不過是我們的比喻 人物是杜撰的 匆忙是假裝的 攜帶的也不是人的訊息

為什么使用grpc

主要包括以下兩點原因:

  • protocl buffer一種高效的序列化結構。
  • 支持http 2.0標準化協議。
  • 很對人經常拿thrift跟grpc比較,現在先不發表任何看法,后續會深入thrift進行介紹。

    http/2

    HTTP/2 enables a more efficient use of network resources and a reduced perception of latency by introducing header field compression and allowing multiple concurrent exchanges on the same connection… Specifically, it allows interleaving of request and response messages on the same connection and uses an efficient coding for HTTP header fields. It also allows prioritization of requests, letting more important requests complete more quickly, further improving performance.

    The resulting protocol is more friendly to the network, because fewer TCP connections can be used in comparison to HTTP/1.x. This means less competition with other flows, and longer-lived connections, which in turn leads to better utilization of available network capacity. Finally, HTTP/2 also enables more efficient processing of messages through use of binary message framing.

    http/2帶來了網絡性能的巨大提升,下面列舉一些個人覺得比較重要的細節:

  • http/2對每個源只需創建一個持久連接,在這一個連接內,可以并行的處理多個請求和響應,而且做到不相互影響。
  • 允許客戶端和服務端實現自己的數據流和連接流控制,這對我們傳輸大數據非常有幫助。
  • 更多細節,請參考文章末尾的鏈接,當然,后續也會專門介紹。

    準備工作

    大家可以參考protobuf的介紹,具體包括:

  • 安裝Go的開發環境,因為后續是基于Go語言的開發項目
  • 安裝protocol-buffers
  • 安裝protoc-gen-go,用于自動生成源碼
  • 生成源碼的命令如下,其中,--go_out用于指定生成源碼的保存路徑;而-I是-IPATH的簡寫,用于指定查找import文件的路徑,可以指定多個;最后的order是編譯的grpc文件的存儲路徑。

    protoc -I proto/ proto/order.proto --go_out=plugins=grpc:order

    protocol buffer

    google開發的高效、跨平臺的數據傳輸格式。當然,本質還是數據傳輸結構。但google賦予了它豐富的功能,比如import、package、消息嵌套等等。import用于引入別的.proto文件;package用于定義命名空間,轉換到go源碼中就是包名;repeated用于定義重復的數據;enum用于定義枚舉類型等。

    .proto內字段的基本定義:

    type name = tag;

    Protocol buffer本身不包含類型的描述信息,因此獲取了沒有.proto描述文件的二進制信息是毫無用處的,我們很難提取出非常有用的信息。Go語言complier生成的文件后綴是.pb.go,它自動生成了set、get以及read、write方法,我們可以很方便的序列化數據。

    下面我們定義一個創建訂單的.proto文件,概括的描述:buyerID在device上支付amount買sku商品。

  • 聲明版本為proto3,package是order。
  • 設備類型定義為枚舉類型,包括ANDROID和IOS兩種,而且類型被嵌套聲明在OrderParams內。
  • sku聲明為repeated,因為用戶可能購買多個商品。
  • OrderResult為響應的消息體結構,包括生成的訂單號和處理的響應碼。
  • service聲明了order要提供的服務。當前僅僅實現一個simple RPC:客戶端使用OrderParams參數請求RPC服務器,收到OrderResult作為響應。
  • syntax = "proto3"; package order;service Order {//a simple RPC//create new orderrpc Add (OrderParams) returns (OrderResult) {} }message OrderParams {string amount = 1; //訂單金額int64 buyerID = 2; //購買用戶IDenum Device {IOS = 0;ANDROID = 1;}Device device = 3;repeated Sku sku = 4; }message Sku {int32 num = 1;string skuId = 2;int32 unitPrice = 3; }message OrderResult {int32 statusCode = 1;string orderID = 2; }

    grpc接口

    通過定義的.proto文件生成grpc client和server端實現的接口類型。生成的內容主要包括:

  • protocol buffer各種消息類型的序列化操作
  • grpc client實現的接口類型,以及client實現的grpc方法
  • grpc server待實現的接口類型
  • service處理流程

    第一步. 服務端為每個接收的連接創建單獨的goroutine進行處理。

    第二步. 自動生成的代碼中,聲明了服務的具體描述,也是該服務的“路由”。包括服務名稱ServiceName以Methods、Streams。當rpc接收到新的數據時,會根據路由執行對應的方法。因為我們的設定沒有處理流的場景,所以Streams為空的結構體。

    代碼中的服務名稱被指定為:order.Order,對應創建訂單的方法是:Add。

    var _Order_serviceDesc = grpc.ServiceDesc{ServiceName: "order.Order",HandlerType: (*OrderServer)(nil),Methods: []grpc.MethodDesc{{MethodName: "Add",Handler: _Order_Add_Handler,},},Streams: []grpc.StreamDesc{},Metadata: "order.proto", }

    第三步. 將路由注冊到rpc服務中。如下所示,就是將上述的路由轉換為map對應關系的過程。類比restful風格的接口定義,等價于/order/這種請求都由這個service來進行處理。

    最終將service注冊到gRPC server上。同時,我們可以逆向猜出服務的處理過程:通過請求的路徑獲取service,然后通過MethodName調用相應的處理方法。

    srv := &service{server: ss,md: make(map[string]*MethodDesc),sd: make(map[string]*StreamDesc),mdata: sd.Metadata, } for i := range sd.Methods {d := &sd.Methods[i]srv.md[d.MethodName] = d } for i := range sd.Streams {d := &sd.Streams[i]srv.sd[d.StreamName] = d } s.m[sd.ServiceName] = srv

    第四步. gRPC服務處理請求。通過請求的:path,獲取對應的service和MethodName進行處理。

    service := sm[:pos] method := sm[pos+1:]if srv, ok := s.m[service]; ok {if md, ok := srv.md[method]; ok {s.processUnaryRPC(t, stream, srv, md, trInfo)return}if sd, ok := srv.sd[method]; ok {s.processStreamingRPC(t, stream, srv, sd, trInfo)return} }

    通過結合protoc自動生成的client端代碼,無需抓包,我們就可以推斷出path的格式,以及系統是如何處理路由的。代碼中定義的:/order.Order/Add就是依據。

    func (c *orderClient) Add(ctx context.Context, in *OrderParams, opts ...grpc.CallOption) (*OrderResult, error) {out := new(OrderResult)err := c.cc.Invoke(ctx, "/order.Order/Add", in, out, opts...)if err != nil {return nil, err}return out, nil }

    創建訂單

    為了簡單起見,我們只保證訂單的唯一性。這里我們實現一個簡易版本,而且也不做過多介紹。感興趣的同學可以移步到另一篇文章:探討分布式ID生成系統去了解,畢竟不應該是本節的重心。

    //上次創建訂單使用的毫秒時間 var lastTimestamp = time.Now().UnixNano() / 1000000 var sequence int64const MaxSequence = 4096// 42bit分配給毫秒時間戳 // 12bit分配給序列號,每4096就重新開始循環 // 10bit分配給機器ID func CreateOrder(nodeId int64) string {currentTimestamp := getCurrentTimestamp()if currentTimestamp == lastTimestamp {sequence = (sequence + 1) % MaxSequenceif sequence == 0 {currentTimestamp = waitNextMillis(currentTimestamp)}} else {sequence = 0}orderId := currentTimestamp << 22orderId |= nodeId << 10orderId |= sequencereturn strings.ToUpper(fmt.Sprintf("%x", orderId)) }func getCurrentTimestamp() int64 {return time.Now().UnixNano() / 1000000 }func waitNextMillis(currentTimestamp int64) int64 {for currentTimestamp == lastTimestamp {currentTimestamp = getCurrentTimestamp()}return currentTimestamp }

    運行系統

    創建服務端代碼。注意:使用grpc提供的默認選項,其實是很危險的行為。在生產開發中,被不熟悉的默認選項坑到的情況比比皆是。這里的代碼不要作為后續生產環境開發的參考。服務端的代碼相比客戶端要復雜一點,需要我們去實現處理請求的接口。

    type Order struct { }func (o *Order) Add(ctx context.Context, in *order.OrderParams) (*order.OrderResult, error) {return &order.OrderResult{OrderID: util.CreateOrder(1),}, nil }func main() {lis, err := net.Listen("tcp", "127.0.0.1:10000")if err != nil {log.Fatalf("Failed to listen: %v", err)}grpcServer := grpc.NewServer()order.RegisterOrderServer(grpcServer, &Order{})grpcServer.Serve(lis) }

    客戶端的代碼非常簡單,構造參數,處理返回就Ok了。

    func createOrder(client order.OrderClient, params *order.OrderParams) {ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)defer cancel()orderResult, err := client.Add(ctx, params)if err != nil {log.Fatalf("%v.GetFeatures(_) = _, %v: ", client, err)}log.Println(orderResult) }func main() {conn, err := grpc.Dial("127.0.0.1:10000")if err != nil {log.Fatalf("fail to dial: %v", err)}defer conn.Close()client := order.NewOrderClient(conn)orderParams := &order.OrderParams{BuyerID: 10318003,}createOrder(client, orderParams) }

    總結

    文章介紹了gRPC的入門知識,包括protocol buffer以及http/2,gRPC封裝了很多東西,對于一般場合,我們只需要指定配置,實現接口就可以了,非常簡單。

    在入門的介紹里,大家會覺得gRPC不就跟RESTFUL請求一樣嗎?確實是,我也這樣覺得。但存在一個最直觀的優點:通過使用gRPC,可以將復雜的接口調用關系封裝在SDK中,直接提供給第三方使用,而且還能有效避免錯誤調用接口的情況。

    如果gRPC只能這樣的話,它就太失敗了,他用HTTP/2簡直就是用來打蚊子的,讓我們后續繼續深入了解吧。


    參考文章:

  • gRPC:Google 開源的基于 HTTP/2 和 ProtoBuf 的通用 RPC 框架
  • GRPC
  • HTTP/2 簡介
  • http2
  • 總結

    以上是生活随笔為你收集整理的gRPC入门的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。