go grpc高級用法

文章目錄

    • 錯誤處理
      • 常規用法
      • 進階用法
      • 原理
    • 多路復用
    • 元數據
    • 負載均衡
    • 壓縮數據

錯誤處理

gRPC 一般不在 message 中定義錯誤。畢竟每個 gRPC 服務本身就帶一個 error 的返回值,這是用來傳輸錯誤的專用通道。gRPC 中所有的錯誤返回都應該是 nil 或者 由 status.Status 產生的一個error。這樣error可以直接被調用方Client識別。

常規用法

當遇到一個go錯誤的時候,直接返回是無法被下游client識別的。

恰當的做法是
調用 status.New 方法,并傳入一個適當的錯誤碼,生成一個 status.Status 對象
調用該 status.Err 方法生成一個能被調用方識別的error,然后返回
st := status.New(codes.NotFound, “some description”)
err := st.Err()
傳入的錯誤碼是 codes.Code 類型。

此外還有更便捷的辦法:使用 status.Error。它避免了手動轉換的操作。

err := status.Error(codes.NotFound, "some description")

進階用法

上面的錯誤有個問題,就是 code.Code 定義的錯誤碼只有固定的幾種,無法詳盡地表達業務中遇到的錯誤場景。

gRPC 提供了在錯誤中補充信息的機制:status.WithDetails 方法

Client 通過將 error 重新轉換位 status.Status ,就可以通過 status.Details 方法直接獲取其中的內容。

status.Detials 返回的是個slice, 是interface{}的slice,然而go已經自動做了類型轉換,可以通過斷言直接使用。

服務端示例

  • 生成一個 status.Status 對象
  • 填充錯誤的補充信息
// 生成一個 status.Status 
st := status.New(codes.ResourceExhausted, "Request limit exceeded.")
// 填充錯誤的補充信息 WithDetails
ds, err := st.WithDetails(&epb.QuotaFailure{Violations: []*epb.QuotaFailure_Violation{{Subject:     fmt.Sprintf("name:%s", in.Name),Description: "Limit one greeting per person",}},},
)
if err != nil {return nil, st.Err()
}
return nil, ds.Err()

客戶端的示例

  • 調用RPC錯誤后,解析錯誤信息
  • 通過斷言直接獲取錯誤詳情
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "world"})
// 調用 RPC 如果遇到錯誤就對錯誤處理
if err != nil {// 轉換錯誤s := status.Convert(err)// 解析錯誤信息for _, d := range s.Details() {// 通過斷言直接使用switch info := d.(type) {case *epb.QuotaFailure:log.Printf("Quota failure: %s", info)default:log.Printf("Unexpected type: %s", info)}}
}

原理

這個錯誤是如何傳遞給調用方Client的呢?

是放到 metadata中的,而metadata是放到HTTP的header中的。

metadata是key:value格式的數據。錯誤的傳遞中,key是個固定值:grpc-status-details-bin。

而value,是被proto編碼過的,是二進制安全的。

目前大多數語言都實現了這個機制。

多路復用

同一臺服務器上的多個RPC服務的多路復用,比如同時保存一個訂單的存根、一個歡迎的存根因為多個RPC服務運行在一個服務端上,所以客戶端的多個存根之間是可以共享gRPC連接的
服務端代碼

func main() {lis, err := net.Listen("tcp", port)if err != nil {log.Fatalf("failed to listen: %v", err)}grpcServer := grpc.NewServer() // 注冊進訂單服務ordermgt_pb.RegisterOrderManagementServer(grpcServer, &orderMgtServer{}) // 注冊進歡迎服務hello_pb.RegisterGreeterServer(grpcServer, &helloServer{}) 
}

客戶端代碼

func main() {conn, err := grpc.Dial(address, grpc.WithInsecure())if err != nil {log.Fatalf("did not connect: %v", err)}defer conn.Close()// 訂單服務建立實例連接orderManagementClient := pb.NewOrderManagementClient(conn)ctx, cancel := context.WithTimeout(context.Background(), time.Second)defer cancel()order1 := pb.Order{Id: "101", Items:[]string{"iPhone XS", "Mac Book Pro"}, Destination:"San Jose, CA", Price:2300.00}res, addErr := orderManagementClient.AddOrder(ctx, &order1)// 歡迎服務建立實例連接helloClient := hwpb.NewGreeterClient(conn)hwcCtx, hwcCancel := context.WithTimeout(context.Background(), time.Second)defer hwcCancel()helloResponse, err := helloClient.SayHello(hwcCtx, &hwpb.HelloRequest{Name: "gRPC Up and Running!"})fmt.Println("Greeting: ", helloResponse.Message)
}

元數據

在多個微服務的調用當中,信息交換常常是使用方法之間的參數傳遞的方式,但是在有些場景下,一些信息可能和 RPC 方法的業務參數沒有直接的關聯,所以不能作為參數的一部分,在 gRPC 中,可以使用元數據來存儲這類信息。

元數據創建

// 方法1
md := metadata.Pairs("1", "v1","1", "v2",	// 方法1會把相同的鍵的字段合并,[ ]string{"v1","v2"}"2", "v3",)
// 方法2
md := metadata.New(map[string]string{"1":"v1","2":"v2"})

客戶端收發

在context中設置的元數據會轉換成線路層的gRPC頭信息和 trailer

客戶端發送這些頭信息,收件方會以頭信息的形式接收他們

	// 創建元數據md := metadata.Pairs("timestamp", time.Now().Format(time.StampNano),"kn", "vn",)// 創建新元數據的上下文,這種方法會替換掉已有的上下文mdCtx := metadata.NewOutgoingContext(context.Background(), md)// 這種方法是將元數據附加到已有的上下文ctxA := metadata.AppendToOutgoingContext(mdCtx, "k1", "v1", "k1", "v2", "k2", "v3")// 定義頭信息和 trailer,可以用來接收元數據var header, trailer metadata.MDorder1 := pb.Order{Id: "101", Items: []string{"iPhone XS", "Mac Book Pro"}, Destination: "San Jose, CA", Price: 2300.00}res, _ := client.AddOrder(ctxA, &order1, grpc.Header(&header), grpc.Trailer(&trailer))log.Print("AddOrder Response -> ", res.Value)// 獲取頭信息head, err := res.Header()// 獲取trailertrail, err := res.Trailer()

服務端收發

// 從上下文中獲取元數據列表
md, metadataAvailable := metadata.FromIncomingContext(ctx)if !metadataAvailable {return nil, status.Errorf(codes.DataLoss, "UnaryEcho: failed to get metadata")}
// 操作元數據邏輯if t, ok := md["timestamp"]; ok {fmt.Printf("timestamp from metadata:\n")for i, e := range t {fmt.Printf("====> Metadata %d. %s\n", i, e)}}// 創建元數據
header := metadata.New(map[string]string{"location": "San Jose", "timestamp": time.Now().Format(time.StampNano)})
// 發送頭信息
grpc.SendHeader(ctx, header)
trailer := metadata.Pairs("status","ok")
// 設置trailer
grpc.SetTrailer(ctx,trailer)

負載均衡

負載均衡器代理

也就是說后端的結構對gRPC客戶端是不透明的,客戶端只需要知道均衡器的斷點就可以了,比如NGINX代理、Envoy代理

客戶端負載均衡

func main(){roundrobinConn, err := grpc.Dial(address,grpc.WithBalancerName("round_robin"), 	// 指定負載均衡的算法// 默認是"pick_first",也就是從服務器列表中第一個服務端開始嘗試發送請求,成功則后續所有RPC都發往這個服務器// "round_robin"輪詢調度算法,連接所有地址,每次向后端發送一個RPCgrpc.WithInsecure(),)if err != nil {log.Fatalf("did not connect: %v", err)}defer roundrobinConn.Close()// 起10個RPC調度任務makeRPCs(roundrobinConn, 10)
}func makeRPCs(cc *grpc.ClientConn, n int) {hwc := ecpb.NewEchoClient(cc)for i := 0; i < n; i++ {callUnary(hwc)}
}func callUnary(c ecpb.EchoClient) {ctx, cancel := context.WithTimeout(context.Background(), time.Second)defer cancel()}

在這里插入圖片描述

壓縮數據

在服務端會對已注冊的壓縮器自動解碼,響應時自動編碼
始終從客戶端獲取指定的壓縮方法,如果沒被注冊就會返回Unimplemented

func main() {conn, err := grpc.Dial(address, grpc.WithInsecure())defer conn.Close()client := pb.NewOrderManagementClient(conn)ctx, cancel := context.WithTimeout(context.Background(), time.Second * 5)defer cancel()order1 := pb.Order{Id: "101", Items:[]string{"iPhone XS", "Mac Book Pro"}, Destination:"San Jose, CA", Price:2300.00}// 通過 grpc.UseCompressor(gzip.Name) 就可以輕松壓縮數據res, _ := client.AddOrder(ctx, &order1, grpc.UseCompressor(gzip.Name))
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/208352.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/208352.shtml
英文地址,請注明出處:http://en.pswp.cn/news/208352.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

如何克服微服務測試的挑戰,并最大化收益

多年來&#xff0c;微服務一直是行業趨勢&#xff0c;但組織卻未能從該方法中獲益&#xff0c;并因發布失敗而苦苦掙扎。這些失敗通常歸結為測試服務之間的接口以獲得預期的質量、安全性和性能的困難。 最終&#xff0c;未能以足夠穩健的方式測試這些 API。一線希望是遺留 SOA…

cookie總結

cookie和session&#xff1a; 一、Cookie和Session二、使用Cookie保存用戶上次的訪問時間。三、Cookie常用方法總結亂碼問題解決&#xff1a; 一、Cookie和Session 會話&#xff1a;用戶從打開瀏覽器到關閉的整個過程就叫1次會話。 比如有的網站登錄過一次&#xff0c;下次再進…

Gitleaks - 一款高效的Github倉庫敏感信息泄露查詢工具

Gitleaks - 一款高效的Github倉庫敏感信息泄露查詢工具 1.工具概述2.安裝3.參數解析4.使用1.工具概述 Gitleaks 是一種 SAST 工具,用于檢測和防止 git 存儲庫中的硬編碼機密,如密碼、API 密鑰和令牌 Gitleaks 是一個開源工具,用于檢測和防止簽入 Git 存儲庫的機密(密碼/A…

使用 Kubernetes 為 CI/CD 流水線打造高效可靠的臨時環境

介紹 在不斷發展的科技世界中&#xff0c;快速構建高質量的軟件至關重要。在真實環境中測試應用程序是及早發現和修復錯誤的關鍵。但是&#xff0c;在真實環境中設置 CI/CD 流水線進行測試可能既棘手又昂貴。 Kubernetes 是一個流行的容器編排平臺&#xff0c;提供臨時環境解決…

【qt】Qt+OpenCv讀取帶有中文路徑的圖片

【opencv4.5.1版本】下載exe解壓即可。。。https://opencv.org/releases/page/2/ 【qt5.15.2】 pro文件 QT core guigreaterThan(QT_MAJOR_VERSION, 4): QT widgetsCONFIG c17# You can make your code fail to compile if it uses deprecated APIs. # In order to …

YOLOv8配置文件yolov8.yaml解讀

&#x1f368; 本文為&#x1f517;365天深度學習訓練營 中的學習記錄博客&#x1f356; 原作者&#xff1a;K同學啊 | 接輔導、項目定制 位置 該文件的位置位于 ./ultralytics/cfg/models/v8/yolov8.yaml 模型參數配置 # Parameters nc: 80 # number of classes scales: #…

碩士畢業論文格式修改要點_word

目錄 0、最開始要做的事情1、更改樣式&#xff08;先善器&#xff09;2、多級標題&#xff08;解決自動更新問題必要的基礎設置&#xff09;2、插入圖片&#xff08;1&#xff09;設置一個圖片樣式——“無間隔”&#xff08;2&#xff09;插入題注&#xff08;3&#xff09;修…

redis.conf詳解之replica-read-only

本文基于 redis_version:6.2.5 用法 控制從節點允許讀寫操作&#xff0c;還是讀操作。 replica-read-only yes 1 注意事項&#xff1a; 默認yes&#xff0c;只讀操作。 replica-read-only支持參數[yes,no]&#xff0c;no 支持讀寫操作。 這里說的從節點&#xff0c;是普通…

【flink番外篇】1、flink的23種常用算子介紹及詳細示例(完整版)

Flink 系列文章 一、Flink 專欄 Flink 專欄系統介紹某一知識點&#xff0c;并輔以具體的示例進行說明。 1、Flink 部署系列 本部分介紹Flink的部署、配置相關基礎內容。 2、Flink基礎系列 本部分介紹Flink 的基礎部分&#xff0c;比如術語、架構、編程模型、編程指南、基本的…

小白學java棧的經典算法問題——第四關白銀挑戰

內容1.括號匹配問題2.最小棧3.最大棧 1.括號匹配問題 棧的典型題目還是非常明顯的&#xff0c;括號匹配、表達式計算等等幾乎都少不了棧&#xff0c;本小節我們就看兩個最經典的問題 首先是LeetCode20,鏈接 本道題還是比較簡單的&#xff0c;其中比較麻煩的是如何判斷兩個符…

力扣面試題 08.12. 八皇后(java回溯解法)

Problem: 面試題 08.12. 八皇后 文章目錄 題目描述思路解題方法復雜度Code 題目描述 思路 八皇后問題的性質可以利用回溯來解決&#xff0c;將大問題具體分解成如下待解決問題&#xff1a; 1.以棋盤的每一行為回溯的決策階段&#xff0c;判斷當前棋盤位置能否放置棋子 2.如何判…

hbuilder + uniapp +vue3 開發微信云小程序

1、創建項目&#xff1a; 2、創建項目完成的默認目錄結構&#xff1a; 3、在根目錄新建一個文件夾cloudFns&#xff08;文件名字隨便&#xff09;&#xff0c;存放云函數源碼&#xff1a; 4、修改manifest.json文件&#xff1a;添加 小程序 appid和cloudfunctionRoot&#xff0…

python的websocket方法教程

WebSocket是一種網絡通信協議&#xff0c;它在單個TCP連接上提供全雙工的通信信道。在本篇文章中&#xff0c;我們將探討如何在Python中使用WebSocket實現實時通信。 websockets是Python中最常用的網絡庫之一&#xff0c;也是websocket協議的Python實現。它不僅作為基礎組件在…

pyside/qt03——人機協同的編程教學—直接面向chatGPT實戰開發(做中學,事上練)

先大概有個草圖框架&#xff0c;一點點豐富 我糾結好久&#xff0c;直接用Python寫UI代碼 還是用designer做UI 再轉Python呢&#xff0c; 因為不管怎么樣都要轉成Python代碼&#xff0c; 想了想還是學一下designer吧&#xff0c;有個中介&#xff0c;有直觀理解。 直接這樣也可…

智能優化算法應用:基于食肉植物算法無線傳感器網絡(WSN)覆蓋優化 - 附代碼

智能優化算法應用&#xff1a;基于食肉植物算法無線傳感器網絡(WSN)覆蓋優化 - 附代碼 文章目錄 智能優化算法應用&#xff1a;基于食肉植物算法無線傳感器網絡(WSN)覆蓋優化 - 附代碼1.無線傳感網絡節點模型2.覆蓋數學模型及分析3.食肉植物算法4.實驗參數設定5.算法結果6.參考…

設計并實現一個多線程圖書館管理系統,涉及數據庫操作

沒有實現全部功能&#xff0c;希望路過的大佬&#xff0c;可以實現全部功能&#xff0c;在評論區聊聊 創建數據庫library-demo CREATE DATABASE library-demo創建圖書表book CREATE TABLE book (bookId int(11) NOT NULL AUTO_INCREMENT COMMENT 圖書ID,bookName varchar(15)…

QUIC協議對比TCP網絡性能測試模擬弱網測試

QUIC正常外網壓測數據---時延diff/ms如下圖&#xff1a; QUIC弱網外網壓測數據 TCP正常外網壓測數據 TCP弱網外網壓測數據 結論&#xff1a; 在弱網情況下&#xff0c;TCP和QUIC協議的表現會有所不同。下面是它們在弱網環境中的性能對比&#xff1a; 連接建立&#xff1a;…

HarmonyOS創建JavaScript(類 Web開發模式)項目

上文 HarmonyOS帶大家創建自己的第一個Page頁面并實現路由跳轉(ArkTS)帶大家創建了我們項目中第一個自己創建的page 并完成了一個跳轉邏輯的編寫 上文的開發模式是 ArkTS 的 也被稱為 聲明式開發范式 還有一種 javaScript的 類Web開發模式 這種方式就類似于我們傳統的前端開發模…

基于微群機器人的二次開發

請求URL&#xff1a; http://域名地址/modifyGroupName 請求方式&#xff1a; POST 請求頭Headers&#xff1a; Content-Type&#xff1a;application/jsonAuthorization&#xff1a;login接口返回 參數&#xff1a; 參數名必選類型說明wId是String登錄實例標識chatRoom…

讀書筆記-《數據結構與算法》-摘要2[冒泡排序]

冒泡排序 核心&#xff1a;冒泡&#xff0c;持續比較相鄰元素&#xff0c;大的挪到后面&#xff0c;因此大的會逐步往后挪&#xff0c;故稱之為冒泡。 public class BubbleSort {public static void main(String[] args) {int unsortedArray[] new int[]{6, 5, 3, 1, 8, 7, 2…