欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

go grpc高級用法

 更新時間:2024年01月28日 11:34:16   作者:過去日記  
RPC是遠程過程調(diào)用,可以像調(diào)用本地服務一樣取調(diào)用遠程服務,本文主要介紹了go grpc高級用法,具有一定的參考價值,感興趣的可以了解一下

錯誤處理

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

常規(guī)用法

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

恰當?shù)淖龇ㄊ?/strong>:
調(diào)用 status.New 方法,并傳入一個適當?shù)腻e誤碼,生成一個 status.Status 對象
調(diào)用該 status.Err 方法生成一個能被調(diào)用方識別的error,然后返回
st := status.New(codes.NotFound, “some description”)
err := st.Err()
傳入的錯誤碼是 codes.Code 類型。

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

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

進階用法

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

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

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

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

服務端示例

  • 生成一個 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()

客戶端的示例

  • 調(diào)用RPC錯誤后,解析錯誤信息
  • 通過斷言直接獲取錯誤詳情
r, err := c.SayHello(ctx, &pb.HelloRequest{Name: "world"})
// 調(diào)用 RPC 如果遇到錯誤就對錯誤處理
if err != nil {
    // 轉(zhuǎn)換錯誤
    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)
        }
    }
}

原理

這個錯誤是如何傳遞給調(diào)用方Client的呢?

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

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

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

目前大多數(shù)語言都實現(xiàn)了這個機制。

多路復用

同一臺服務器上的多個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)
}

元數(shù)據(jù)

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

元數(shù)據(jù)創(chuàng)建

// 方法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"})

客戶端收發(fā)

在context中設置的元數(shù)據(jù)會轉(zhuǎn)換成線路層的gRPC頭信息和 trailer

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

	// 創(chuàng)建元數(shù)據(jù)
	md := metadata.Pairs(
		"timestamp", time.Now().Format(time.StampNano),
		"kn", "vn",
	)
	// 創(chuàng)建新元數(shù)據(jù)的上下文,這種方法會替換掉已有的上下文
	mdCtx := metadata.NewOutgoingContext(context.Background(), md)
	// 這種方法是將元數(shù)據(jù)附加到已有的上下文
	ctxA := metadata.AppendToOutgoingContext(mdCtx, "k1", "v1", "k1", "v2", "k2", "v3")

	// 定義頭信息和 trailer,可以用來接收元數(shù)據(jù)
	var header, trailer metadata.MD

	order1 := 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()
	// 獲取trailer
	trail, err := res.Trailer()

服務端收發(fā)

// 從上下文中獲取元數(shù)據(jù)列表
md, metadataAvailable := metadata.FromIncomingContext(ctx)
	if !metadataAvailable {
		return nil, status.Errorf(codes.DataLoss, "UnaryEcho: failed to get metadata")
	}
// 操作元數(shù)據(jù)邏輯
	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)
		}
	}

// 創(chuàng)建元數(shù)據(jù)
header := metadata.New(map[string]string{"location": "San Jose", "timestamp": time.Now().Format(time.StampNano)})
// 發(fā)送頭信息
grpc.SendHeader(ctx, header)
trailer := metadata.Pairs("status","ok")
// 設置trailer
grpc.SetTrailer(ctx,trailer)

負載均衡

負載均衡器代理

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

客戶端負載均衡

func main(){
  roundrobinConn, err := grpc.Dial(
		address,
		grpc.WithBalancerName("round_robin"), 	// 指定負載均衡的算法
    // 默認是"pick_first",也就是從服務器列表中第一個服務端開始嘗試發(fā)送請求,成功則后續(xù)所有RPC都發(fā)往這個服務器
    // "round_robin"輪詢調(diào)度算法,連接所有地址,每次向后端發(fā)送一個RPC
		grpc.WithInsecure(),
	)
	if err != nil {
		log.Fatalf("did not connect: %v", err)
	}
	defer roundrobinConn.Close()
	// 起10個RPC調(diào)度任務
	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()
  
}

在這里插入圖片描述

壓縮數(shù)據(jù)

在服務端會對已注冊的壓縮器自動解碼,響應時自動編碼
始終從客戶端獲取指定的壓縮方法,如果沒被注冊就會返回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) 就可以輕松壓縮數(shù)據(jù)
	res, _ := client.AddOrder(ctx, &order1, grpc.UseCompressor(gzip.Name))
}

到此這篇關(guān)于go grpc高級用法的文章就介紹到這了,更多相關(guān)go grpc內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Go語言判斷文件或文件夾是否存在的方法

    Go語言判斷文件或文件夾是否存在的方法

    這篇文章主要介紹了Go語言判斷文件或文件夾是否存在的方法,結(jié)合具體實例形式對比分析了Go語言針對文件與目錄判斷的操作技巧與相關(guān)注意事項,需要的朋友可以參考下
    2017-05-05
  • Golang如何交叉編譯各個平臺的二進制文件詳解

    Golang如何交叉編譯各個平臺的二進制文件詳解

    這篇文章主要給大家介紹了關(guān)于Golang如何交叉編譯各個平臺的二進制文件的相關(guān)資料,并介紹了golang如何讓編譯生產(chǎn)的二進制文件變小,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2018-08-08
  • go語言中range用法

    go語言中range用法

    這篇文章主要介紹了go語言中range用法,實例分析了Go語言中range的功能及使用技巧,需要的朋友可以參考下
    2015-03-03
  • go語言?nil使用避坑指南

    go語言?nil使用避坑指南

    這篇文章主要為大家介紹了go語言?nil使用避坑指南詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-09-09
  • 基于go手動寫個轉(zhuǎn)發(fā)代理服務的代碼實現(xiàn)

    基于go手動寫個轉(zhuǎn)發(fā)代理服務的代碼實現(xiàn)

    這篇文章主要介紹了基于go手動寫個轉(zhuǎn)發(fā)代理服務的代碼實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2019-02-02
  • go語言標準庫fmt包的一鍵入門

    go語言標準庫fmt包的一鍵入門

    這篇文章主要為大家介紹了go語言標準庫fmt包的一鍵入門使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪
    2022-08-08
  • Go中init()執(zhí)行順序詳解

    Go中init()執(zhí)行順序詳解

    go允許定義多個init(),多個init()會涉及到執(zhí)行先后的問題,本文將詳細講解Go中init()執(zhí)行順序,感興趣的朋友一起看看吧
    2022-09-09
  • Golang并發(fā)讀取文件數(shù)據(jù)并寫入數(shù)據(jù)庫的項目實踐

    Golang并發(fā)讀取文件數(shù)據(jù)并寫入數(shù)據(jù)庫的項目實踐

    本文主要介紹了Golang并發(fā)讀取文件數(shù)據(jù)并寫入數(shù)據(jù)庫的項目實踐,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧
    2022-06-06
  • 深入剖析Go語言中的Select語句

    深入剖析Go語言中的Select語句

    select是Go中的一個控制結(jié)構(gòu),類似于switch語句,本文主要介紹了深入剖析Go語言中的Select語句,具有一定的參考價值,感興趣的可以了解一下
    2023-12-12
  • go語言基礎 seek光標位置os包的使用

    go語言基礎 seek光標位置os包的使用

    這篇文章主要介紹了go語言基礎 seek光標位置os包的使用詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-05-05

最新評論