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

歡迎訪問 生活随笔!

生活随笔

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

编程问答

go语言入门经典_Go 语言中的 gRPC 基础入门

發布時間:2025/4/16 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 go语言入门经典_Go 语言中的 gRPC 基础入门 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

01

為什么使用 gRPC?

借助 gRPC,我們可以在 .proto 文件中一次定義我們的服務,并以 gRPC 支持的任何語言生成客戶端和服務器代碼,無論是在大型數據中心內的服務器,還是在個人的電腦的環境中,這些客戶端和服務器代碼都可以運行 –? gRPC 可以為您處理不同語言和環境之間的通信。我們還獲得了使用 protocol buffers 的所有優點,包括有效的序列化,簡單的 IDL 和容易的接口更新。

我們的示例是一個簡單的路由映射應用程序,它使客戶端可以獲取有關其路由功能的信息,創建其路由的摘要以及與服務器和其他客戶端交換路由信息(例如流量更新)。

02

準備工作

  • 安裝 Go 最新正式發行版本。

  • 安裝 protocol buffers 編譯器 protoc。請參考「Protobuf - 更小、更快、更簡單的交互式數據語言」- Part 05。

  • 安裝編譯器 protoc 的 Go 插件。請參考「gRPC 初探與簡單使用」- Part 04。

  • git clone 示例代碼,并進入該目錄。

    $ git clone https://github.com/grpc/grpc-go$ cd grpc-go/examples/route_guide

03

定義服務并生成客戶端和服務器代碼

我們的第一步是使用 protocol buffers 定義 gRPC 服務以及方法請求和響應類型。

有關完整的

.proto 文件,請參閱?Part 2?git clone 的代碼 routeguide/route_guide.proto。

要定義服務,請在 .proto 文件中指定一個命名服務:

service RouteGuide { ...}

然后,在服務定義中定義 rpc 方法,并指定它們的請求和響應類型。gRPC 允許您定義四種服務方法,所有這些方法都在 RouteGuide 服務中使用:

  • 一個簡單的 RPC,客戶端使用存根將請求發送到服務器,然后等待響應返回,就像正常的函數調用一樣。

    // Obtains the feature at a given position.rpc GetFeature(Point) returns (Feature) {}
  • 服務器端流式 RPC,客戶端在其中向服務器發送請求,并獲取流以讀取回一系列消息。客戶端從返回的流中讀取,直到沒有更多消息為止。如我們的示例所示,您可以通過在響應類型之前放置?stream 關鍵字來指定服務器端流方法。

    // 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) {}
  • 客戶端流式 RPC,客戶端在其中編寫消息序列,然后再次使用提供的流將其發送到服務器。客戶端寫完消息后,它將等待服務器讀取所有消息并返回其響應。通過將?stream 關鍵字放在請求類型之前,可以指定客戶端流方法。

    // Accepts a stream of Points on a route being traversed, returning a// RouteSummary when traversal is completed.rpc RecordRoute(stream Point) returns (RouteSummary) {}
  • 雙向流式 RPC,雙方都使用讀寫流發送一系列消息。這兩個流是獨立運行的,因此客戶端和服務器可以按照自己喜歡的順序進行讀寫:例如,服務器可以在寫響應之前等待接收所有客戶端消息,或者可以先讀取一條消息再寫入一條消息,或讀寫的其他組合。每個流中的消息順序都會保留。您可以通過在請求和響應之前都放置?stream 關鍵字來指定這種類型的方法。

    // 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 文件還包含用于服務方法中所有請求和響應類型的 protocol buffers message 類型定義-例如,這是 Point message 類型:

    // 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;}

    接下來,我們需要根據 .proto 服務定義生成 gRPC 客戶端和服務器接口。我們使用帶有特殊 gRPC Go 插件的 protocol buffers 編譯器 protoc 來執行此操作。

    在 examples/route_guide 目錄中,運行以下命令:

    $ protoc --go_out=. --go_opt=paths=source_relative \ --go-grpc_out=. --go-grpc_opt=paths=source_relative \ routeguide/route_guide.proto

    運行此命令將在 routeguide 目錄中生成以下文件:

    • route_guide.pb.go,其中包含用于填充,序列化和檢索請求和響應消息類型的所有 protocol buffers 代碼。

    • route_guide_grpc.pb.go,其中包含以下內容:

      • 客戶端使用 RouteGuide 服務中定義的方法調用的接口類型(或存根)。

      • 服務器要實現的接口類型,也具有 RouteGuide 服務中定義的方法。

    04

    創建服務器

    首先,讓我們看一下如何創建 RouteGuide 服務器。

    使我們的 RouteGuide 服務完成其工作包括兩個部分:

    • 實施根據我們的服務定義生成的服務接口:完成我們服務的實際“工作”。

    • 運行 gRPC 服務器以監聽來自客戶端的請求,并將其分派到正確的服務實現。

    您可以在 server/server.go 中找到我們的示例 RouteGuide 服務器。讓我們仔細看看它是如何工作的。

    實現 RouteGuide

    如您所見,我們的服務器具有一個 routeGuideServer 結構體類型,該結構體類型實現了生成的 RouteGuideServer 接口:

    type routeGuideServer struct { ...}...func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) { ...}...func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error { ...}...func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error { ...}...func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error { ...}...

    簡單的 RPC

    routeGuideServer 實現我們所有的服務方法。首先,讓我們看一下最簡單的類型 GetFeature,該類型僅從客戶端獲取一個 Point,然后從其數據庫中的Feature 中返回相應的 Feature 信息。

    func (s *routeGuideServer) GetFeature(ctx context.Context, point *pb.Point) (*pb.Feature, error) { for _, feature := range s.savedFeatures { if proto.Equal(feature.Location, point) { return feature, nil } } // No feature was found, return an unnamed feature return &pb.Feature{Location: point}, nil}

    該方法傳遞了 RPC 和客戶端的 Point protocol buffer 請求的上下文對象。它返回 Feature protocol buffer 對象以及響應信息和錯誤。在該方法中,我們使用適當的信息填充功能,然后將其返回并返回 nil 錯誤,以告知 gRPC 我們已經完成了對 RPC 的處理,并且可以將 Feature 返回給客戶端。

    服務器端流式 RPC

    現在,讓我們看一下其中的流式 RPC。ListFeatures 是服務器端流式 RPC,因此我們需要將多個 Feature 發送回客戶端。

    func (s *routeGuideServer) ListFeatures(rect *pb.Rectangle, stream pb.RouteGuide_ListFeaturesServer) error { for _, feature := range s.savedFeatures { if inRange(feature.Location, rect) { if err := stream.Send(feature); err != nil { return err } } } return nil}

    如您所見,這次我們沒有獲得簡單的請求和響應對象,而是獲得了一個請求對象(客戶端要在其中找到 Feature 的 Rectangle)

    和一個特殊的 RouteGuide_ListFeaturesServer 對象來編寫響應。

    在該方法中,我們填充了我們需要返回的所有 Feature 對象,并使用其 Send() 方法將它們寫入 RouteGuide_ListFeaturesServer。最后,就像在簡單的 RPC 中一樣,我們返回 nil 錯誤來告訴 gRPC 我們已經完成了響應的編寫。如果此調用中發生任何錯誤,我們將返回非 nil 錯誤;gRPC 層會將其轉換為適當的 RPC 狀態,以在線上發送。

    客戶端流式 RPC

    現在,讓我們看一些更復雜的事情:客戶端流方法 RecordRoute,從客戶端獲取 Point 流,并返回一個包含行程信息的 RouteSummary。如您所見,這次方法完全沒有 request 參數。

    相反,它獲得一個?

    RouteGuide_RecordRouteServer 流,服務器可以使用該流來讀取和寫入消息-它可以使用 Recv()?方法接收客戶端消息,并使用SendAndClose()?方法返回其單個響應。

    func (s *routeGuideServer) RecordRoute(stream pb.RouteGuide_RecordRouteServer) error { var pointCount, featureCount, distance int32 var lastPoint *pb.Point startTime := time.Now() for { point, err := stream.Recv() if err == io.EOF { endTime := time.Now() return stream.SendAndClose(&pb.RouteSummary{ PointCount: pointCount, FeatureCount: featureCount, Distance: distance, ElapsedTime: int32(endTime.Sub(startTime).Seconds()), }) } if err != nil { return err } pointCount++ for _, feature := range s.savedFeatures { if proto.Equal(feature.Location, point) { featureCount++ } } if lastPoint != nil { distance += calcDistance(lastPoint, point) } lastPoint = point }}

    在方法主體中,我們使用?

    RouteGuide_RecordRouteServer的 Recv()?方法重復讀取客戶端對請求對象(在本例中為Point)的請求,直到沒有更多消息為止:服務器需要檢查從 Read()?返回的錯誤。每個 call。如果為 nil,則流仍然良好,并且可以繼續讀取;否則為 0。如果是 io.EOF,則消息流已結束,服務器可以返回其 RouteSummary。如果它具有其他值,我們將返回“原樣”錯誤,以便 gRPC 層將其轉換為 RPC 狀態。

    雙向流式 RPC

    最后,讓我們看一下雙向流式 RPC RouteChat()?。

    func (s *routeGuideServer) RouteChat(stream pb.RouteGuide_RouteChatServer) error { for { in, err := stream.Recv() if err == io.EOF { return nil } if err != nil { return err } key := serialize(in.Location) ... // look for notes to be sent to client for _, note := range s.routeNotes[key] { if err := stream.Send(note); err != nil { return err } } }}

    這次,我們獲得一個 RouteGuide_RouteChatServer 流,就像在客戶端流示例中一樣,該流可用于讀取和寫入消息。但是,這次,當客戶端仍在向其消息流中寫入消息時,我們通過方法的流返回值。

    此處的讀寫語法與我們的客戶端流式傳輸方法非常相似,不同之處在于服務器使用流的 Send()?方法而不是 SendAndClose()?,因為服務器正在寫多個響應。盡管雙方總是會按照對方的寫入順序來獲取對方的消息,但是客戶端和服務器都可以以任意順序進行讀取和寫入-流完全獨立地運行。

    啟動服務器

    一旦實現了所有方法,我們還需要啟動 gRPC 服務器,以便客戶端可以實際使用我們的服務。以下代碼段顯示了如何為 RouteGuide 服務執行此操作:

    flag.Parse()lis, err := net.Listen("tcp", fmt.Sprintf("localhost:%d", *port))if err != nil { log.Fatalf("failed to listen: %v", err)}var opts []grpc.ServerOption...grpcServer := grpc.NewServer(opts...)pb.RegisterRouteGuideServer(grpcServer, newServer())grpcServer.Serve(lis)

    構建和啟動服務:

  • 使用以下命令指定我們要用于監聽客戶端請求的端口:

    lis,err:= net.Listen(...)。

  • 使用 grpc.NewServer(...)?創建 gRPC 服務器的實例。

  • 在 gRPC 服務器上注冊我們的服務實現。

  • 使用我們的端口詳細信息在服務器上調用 Serve()?進行阻塞等待,直到進程被殺死或調用 Stop()?為止。

  • 05

    創建客戶端

    在本部分中,我們將研究為 RouteGuide 服務創建 Go 客戶端。

    您可以在 grpc-go/examples/route_guide/client/client.go 中看到我們完整的示例客戶端代碼。

    創建客戶端存根

    要調用服務方法,我們首先需要創建一個 gRPC 通道來與服務器通信。我們通過將服務器地址和端口號傳遞給 grpc.Dial()?來創建它,如下所示:

    var opts []grpc.DialOption...conn, err := grpc.Dial(*serverAddr, opts...)if err != nil { ...}defer conn.Close()

    當服務需要它們時,可以使用 DialOptions 在 grpc.Dial 中設置身份驗證憑據(例如TLS,GCE憑據或JWT憑據)。RouteGuide 服務不需要任何憑據。

    設置 gRPC 通道后,我們需要一個客戶端存根來執行 RPC。我們使用從示例 .proto 文件生成的 pb 包提供的 NewRouteGuideClient 方法獲取它。

    client?:=?pb.NewRouteGuideClient(conn)

    調用服務方法

    現在,讓我們看看我們如何調用我們的服務方法。請注意,在 gRPC-Go 中,RPC 在阻塞/同步模式下運行,這意味著 RPC 調用等待服務器響應,并且將返回響應或錯誤。

    簡單的 RPC

    調用簡單的 RPC GetFeature 幾乎與調用本地方法一樣簡單。

    feature, err := client.GetFeature(context.Background(), &pb.Point{409146138, -746188906})if err != nil { ...}

    如您所見,我們在先前獲得的存根上調用該方法。在我們的方法參數中,我們創建并填充一個請求 protocol buffer 對象(在本例中為 Point)。我們還會傳遞一個 context.Context 對象,該對象可讓我們在必要時更改 RPC 的行為,例如 time-out/cancel 運行中的 RPC。如果調用沒有返回錯誤,那么我們可以從服務器的第一個返回值中讀取響應信息。

    log.Println(feature)

    服務器端流式 RPC

    我們在這里調用服務器端流方法 ListFeatures,該方法返回地理要素流。如果您已經閱讀了創建服務器的內容,那么其中的一些內容可能看起來非常熟悉-流式 RPC 在兩側都以類似的方式實現。

    rect := &pb.Rectangle{ ... } // initialize a pb.Rectanglestream, err := client.ListFeatures(context.Background(), rect)if err != nil { ...}for { feature, err := stream.Recv() if err == io.EOF { break } if err != nil { log.Fatalf("%v.ListFeatures(_) = _, %v", client, err) } log.Println(feature)}

    就像在簡單的 RPC 中一樣,我們為該方法傳遞一個上下文和一個請求。但是,我們沒有取回響應對象,而是取回?

    RouteGuide_ListFeaturesClient 的實例。

    客戶端可以使用 RouteGuide_ListFeaturesClient 流讀取服務器的響應。

    我們使用?

    RouteGuide_ListFeaturesClient 的 Recv()?方法重復讀取服務器對響應 protocol buffer 對象(在本例中為 Feature)的響應,直到沒有更多消息為止:客戶端需要檢查每次返回后從 Recv()?返回的錯誤 err。如果為 nil,則流仍然良好,并且可以繼續讀取;如果是 io.EOF,則消息流已結束;否則,必須存在 RPC 錯誤,該錯誤會通過 err 傳遞。

    客戶端流式 RPC

    客戶端流方法 RecordRoute 與服務器端方法相似,不同之處在于,我們僅向該方法傳遞上下文,并獲取回?

    RouteGuide_RecordRouteClientClient 流,我們可以使用該流來寫入和讀取消息。

    // Create a random number of random pointsr := rand.New(rand.NewSource(time.Now().UnixNano()))pointCount := int(r.Int31n(100)) + 2 // Traverse at least two pointsvar points []*pb.Pointfor i := 0; i < pointCount; i++ { points = append(points, randomPoint(r))}log.Printf("Traversing %d points.", len(points))stream, err := client.RecordRoute(context.Background())if err != nil { log.Fatalf("%v.RecordRoute(_) = _, %v", client, err)}for _, point := range points { if err := stream.Send(point); err != nil { log.Fatalf("%v.Send(%v) = %v", stream, point, err) }}reply, err := stream.CloseAndRecv()if err != nil { log.Fatalf("%v.CloseAndRecv() got error %v, want %v", stream, err, nil)}log.Printf("Route summary: %v", reply)

    RouteGuide_RecordRouteClient 具有一個 Send()?方法,可用于將請求發送到服務器。使用 Send()?完成將客戶的請求寫入流中后,我們需要在流上調用 CloseAndRecv()?,以使 gRPC 知道我們已完成寫入并期望收到響應。我們從 CloseAndRecv()?返回的錯誤中獲取 RPC 狀態。如果狀態為 nil,則 CloseAndRecv()?的第一個返回值將是有效的服務器響應。

    雙向流式 RPC

    最后,讓我們看一下雙向流式 RPC RouteChat()?。

    與 RecordRoute 一樣,我們只向方法傳遞一個上下文對象,然后獲取可用于寫入和讀取消息的流。但是,這一次我們在服務器仍將消息寫入消息流的同時,我們還通過方法的流返回值。

    stream, err := client.RouteChat(context.Background())waitc := make(chan struct{})go func() { for { in, err := stream.Recv() if err == io.EOF { // read done. close(waitc) return } if err != nil { log.Fatalf("Failed to receive a note : %v", err) } log.Printf("Got message %s at point(%d, %d)", in.Message, in.Location.Latitude, in.Location.Longitude) }}()for _, note := range notes { if err := stream.Send(note); err != nil { log.Fatalf("Failed to send a note: %v", err) }}stream.CloseSend()

    除了在完成調用后使用流的 CloseSend()?方法外,此處的讀寫語法與我們的客戶端流方法非常相似。盡管雙方總是會按照對方的寫入順序來獲取對方的消息,但是客戶端和服務器都可以以任意順序進行讀取和寫入-流完全獨立地運行。

    06

    運行程序

    從 examples/route_guide 目錄執行以下命令:

  • 運行服務器:

    $ go run server/server.go
  • 從另一個終端,運行客戶端:

    $ go run client/client.go
  • 輸出內容:

    Getting feature for point (409146138, -746188906)name:"Berkshire Valley Management Area Trail, Jefferson, NJ, USA" location:<409146138 longitude:-746188906>409146138>Getting feature for point (0, 0)location:<>Looking for features within lo:<400000000 longitude:-750000000> hi:<420000000 longitude:-730000000>420000000>400000000>name:"Patriots Path, Mendham, NJ 07945, USA" location:<407838351 longitude:-746143763>407838351>...name:"3 Hasta Way, Newton, NJ 07860, USA" location:<410248224 longitude:-747127767>410248224>Traversing 56 points.Route summary: point_count:56 distance:497013163Got message First message at point(0, 1)Got message Second message at point(0, 2)Got message Third message at point(0, 3)Got message First message at point(0, 1)Got message Fourth message at point(0, 1)Got message Second message at point(0, 2)Got message Fifth message at point(0, 2)Got message Third message at point(0, 3)Got message Sixth message at point(0, 3)

    注意:

    我們已從本頁顯示的客戶端和服務器跟蹤輸出中省略了時間戳。

    07

    總結

    本文開篇先介紹了為什么要使用 gRPC,接著簡述了使用 gRPC 需要做的準備工作,然后通過 gRPC 官方 Go 示例代碼介紹了如何在 .proto 文件中定義服務,如何使用 protoc 編譯器生成客戶端和服務器代碼,如何根據 protoc 編譯器生成的客戶端和服務器代碼創建服務器和客戶端的 4 種形式。


    參考資料:

    https://grpc.io/docs/languages/go/basics/

    總結

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

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

    主站蜘蛛池模板: av每日更新在线观看 | 午夜视频91 | 伊人精品国产 | 特黄特色大片免费播放器使用方法 | 日本人妖在线 | 你懂的av在线 | 五月开心播播网 | 我要爱爱网| 尤物av在线 | 久草视| 国产精品久久网站 | 久久97超碰| 1级性生活片| 色婷婷国产精品视频 | 一区二区三区中文字幕 | 快播91| 3d欧美精品动漫xxxx无尽 | 色老头一区 | 成人在线免费观看网站 | 亚洲精品国产成人 | 日韩在线影院 | 在线观看av免费 | 午夜写真片福利电影网 | 粉嫩av一区二区 | 日韩在线视屏 | 亚洲专区免费 | 日韩精品中文字幕一区 | 91精品久久久久久久久中文字幕 | 欧洲精品一区二区三区久久 | 毛片手机在线 | 手机看片一区二区 | 日韩视频一二三 | 男女性杂交内射妇女bbwxz | 欧美激情午夜 | 新天堂网 | 亚洲福利二区 | 黄色不雅视频 | 亚洲成年人在线 | 亚洲a v网站 | 老司机福利精品 | 二区三区在线视频 | 国产在线观看精品 | 欧美特黄aaa | www.日本在线观看 | 菲律宾黄色片 | 波多野结衣三区 | 青青精品视频 | 国产一区二区成人 | 888奇米影视| 国产v亚洲v天堂无码久久久 | 日韩中出 | 成人手机在线视频 | 久久久久成人精品无码 | 欧美一级爆毛片 | 成人影片网址 | 99精品在线观看视频 | 午夜小网站 | 欧美精品videos另类日本 | 亚洲国产精品va在线看黑人 | 国产免费一区二区三区在线观看 | 伦理黄色片 | 日韩久久高清 | 天降女子| 护士人妻hd中文字幕 | 麻豆国产在线视频 | 日本视频一区二区三区 | 一级免费看 | 一级视频片 | 综合热久久 | av免费大全 | 国产资源免费 | 欧美一级特黄aaaaaa大片在线观看 | www色网| 国产精品九一 | 成人久久久久久久 | avtt在线 | 秋霞在线视频 | 美女扒开大腿让男人桶 | 国产精品亚洲一区二区 | 成人一级网站 | 开心激情五月网 | 中文日韩在线 | 捆绑凌虐一区二区三区 | 欧美aaaaaaa| 男人日女人逼 | 日韩av综合网站 | 国产一区二区欧美 | 国产无套在线观看 | 93久久精品日日躁夜夜躁欧美 | 国产又粗又猛又爽又黄的 | 久久国产网 | 狠狠澡 | 国产精品夜夜爽 | 国产精品欧美一区二区三区 | 丁香六月久久 | 中文字幕一区二区人妻痴汉电车 | 久久久久久成人精品 | 天天天天躁天天爱天天碰2018 | 国产一区两区 |