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

Golang處理gRPC請(qǐng)求/響應(yīng)元數(shù)據(jù)的示例代碼

 更新時(shí)間:2024年03月01日 09:30:04   作者:ghosind  
前段時(shí)間實(shí)現(xiàn)內(nèi)部gRPC框架時(shí),為了實(shí)現(xiàn)在服務(wù)端攔截器中打印請(qǐng)求及響應(yīng)的頭部信息,便查閱了部分關(guān)于元數(shù)據(jù)的資料,因?yàn)橹形木W(wǎng)絡(luò)上對(duì)于該領(lǐng)域的信息較少,于是在這做了一些簡單的總結(jié),需要的朋友可以參考下

元數(shù)據(jù)

gRPC的元數(shù)據(jù)(metadata)是基于HTTP/2頭部實(shí)現(xiàn)的鍵值對(duì)數(shù)據(jù),它通常用來實(shí)現(xiàn)gRPC的鑒權(quán)、鏈路跟蹤以及自定義頭部數(shù)據(jù)等功能。

gRPC的元數(shù)據(jù)分為兩種類型,分別是HeaderTrailer。Header可以由客戶端或服務(wù)端發(fā)送,它在客戶端請(qǐng)求數(shù)據(jù)或服務(wù)器響應(yīng)數(shù)據(jù)前發(fā)送。Trailer是一種特殊的頭部信息,它僅可由服務(wù)端發(fā)送,且位于發(fā)送的數(shù)據(jù)之后。

客戶端處理

在gRPC客戶端中,無論是一元調(diào)用還是流調(diào)用,可以比較簡單地通過google.golang.org/grpc/metadata包提供的AppendToOutgoingContextNewOutgoingContext方法向請(qǐng)求中加入頭部元數(shù)據(jù),例如以下幾種方式:

// 通過metadata創(chuàng)建新的context
md := metadata.Pairs("k1", "v1", "k2", "v2")
ctx := metadata.NewOutgoingContext(ctx, md)

// 或是向context中添加元數(shù)據(jù)
ctx = metadata.AppendToOutgoingContext(ctx, "k3", "v3")

// ... 通過ctx進(jìn)行RPC調(diào)用

對(duì)于服務(wù)端返回的響應(yīng)中的元數(shù)據(jù),一元調(diào)用與流調(diào)用的處理方式就較為不同。對(duì)于一元調(diào)用,需要提前定義好用于存儲(chǔ)元數(shù)據(jù)的變量,然后在調(diào)用時(shí)通過grpc.Headergrpc.Trailer增加調(diào)用的選項(xiàng):

var header, trailer metadata.MD
resp, err := cli.UnaryCall(ctx, req, grpc.Header(&header), grpc.Trailer(&trailer))

// 處理header或trailer

而對(duì)于任意方式的流調(diào)用,都可以簡單地通過流調(diào)用返回流的HeaderTrailer方法獲得元數(shù)據(jù):

stream, err := cli.StreamCall(ctx)

header, err := stream.Header()
trailer, err := stream.Trailer()

服務(wù)端處理

對(duì)于服務(wù)端,請(qǐng)求的元數(shù)據(jù)需要通過metadata.FromIncomingContext從context中獲?。?/p>

// 一元調(diào)用
md, ok := metadata.FromIncomingContext(ctx)

// 流調(diào)用
ctx := stream.Context() // 需要先從流中得到context
md, ok := metadata.FromIncomingContext(ctx)

同樣,在服務(wù)端發(fā)送元數(shù)據(jù)需要根據(jù)一元調(diào)用與流調(diào)用使用不同的方式。對(duì)于一元調(diào)用,可以通過grpc.SendHeader、grpc.SetHeader以及grpc.SetTrailer方法設(shè)置發(fā)送的元數(shù)據(jù),例如:

header := metadata.Pairs("header-key", "header-val")
grpc.SendHeader(ctx, header)
trailer := metadata.Pairs("trailer-key", "trailer-val")
grpc.SetTrailer(ctx, trailer)

對(duì)于上述的SendHeaderSetHeader方法,其區(qū)別為SendHeader方法只能調(diào)用一次,而SetHeader方法將會(huì)對(duì)所有調(diào)用的元數(shù)據(jù)進(jìn)行合并發(fā)送。

對(duì)于流調(diào)用,服務(wù)端發(fā)送元數(shù)據(jù)則是通過流對(duì)象中的上述方法:

header := metadata.Pairs("header-key", "header-val")
stream.SendHeader(,header)
trailer := metadata.Pairs("trailer-key", "trailer-val")
stream.SetTrailer(trailer)

服務(wù)器攔截器處理

對(duì)于gRPC服務(wù)端一元調(diào)用及流調(diào)用攔截器,請(qǐng)求元數(shù)據(jù)的讀取與響應(yīng)元數(shù)據(jù)的發(fā)送與上一節(jié)中的實(shí)現(xiàn)相同,便不再贅述。下面我們將討論一下在攔截器中更新請(qǐng)求元數(shù)據(jù),以及讀取響應(yīng)的元數(shù)據(jù)。

一元調(diào)用攔截器更新請(qǐng)求元數(shù)據(jù)

在服務(wù)端攔截器中更新請(qǐng)求的元數(shù)據(jù),其實(shí)現(xiàn)的方式與客戶端發(fā)送元數(shù)據(jù)類似,即需要通過更新后的元數(shù)據(jù)創(chuàng)建新的context。對(duì)于一元調(diào)用攔截器,其簡單實(shí)現(xiàn)如下所示:

md, ok := metadata.FromIncomingContext(ctx)

md.Append("new-key", "new-value")
ctx = metadata.NewIncomingContext(ctx, md)

resp, err := handler(ctx, req) // 傳遞context至handler中

一元調(diào)用攔截器讀取響應(yīng)元數(shù)據(jù)

對(duì)于一元調(diào)用響應(yīng)的元數(shù)據(jù),gRPC未提供直接訪問的方法響應(yīng)的元數(shù)據(jù)。為了在攔截器中能讀取到響應(yīng)的元數(shù)據(jù),我們可以通過覆蓋原始grpc.ServerTransportStream并對(duì)設(shè)置的元數(shù)據(jù)進(jìn)行備份的方式進(jìn)行實(shí)現(xiàn)。

type WrappedServerTransportStream struct {
  grpc.ServerTransportStream

  header  metadata.MD
  trailer metadata.MD
}

func (s *WrappedServerTransportStream) SendHeader(md metadata.MD) error {
  if err := s.ServerTransportStream.SendHeader(md); err != nil {
    return err
  }

  s.header = md

  return nil
}

// 在需要的情況下繼續(xù)實(shí)現(xiàn)下面的幾個(gè)方法:
// func (s *WrappedServerTransportStream) SetHeader(metadata.MD) error
// func (s *WrappedServerTransportStream) SetTrailer(metadata.MD) error

在定義帶有元數(shù)據(jù)副本的ServerTransportStream實(shí)現(xiàn)后,我們需要通過grpc.ServerTransportStreamFromContext獲取到一元調(diào)用的原始流,在對(duì)其進(jìn)行封裝后,調(diào)用grpc.NewContextWithServerTransportStream創(chuàng)建新的context。

stream := grpc.ServerTransportStreamFromContext(ctx)
wrappedStream := &WrappedServerTransportStream{
  ServerTransportStream: stream,
}
ctx = grpc.NewContextWithServerTransportStream(ctx, wrappedStream)

resp, err := handler(ctx, req)

// 通過wrappedStream.header、wrappedStream.trailer讀取響應(yīng)的元數(shù)據(jù)

需要注意,grpc.ServerTransportStream接口是一個(gè)實(shí)驗(yàn)性的接口,在后續(xù)版本中可能會(huì)被移除,所以本節(jié)中描述的方法在后續(xù)版本中可能不再可用。

流調(diào)用攔截器更新請(qǐng)求元數(shù)據(jù)

而對(duì)于流調(diào)用,gRPC沒有提供修改其context的方法,為了實(shí)現(xiàn)修改流調(diào)用請(qǐng)求元數(shù)據(jù),就需要實(shí)現(xiàn)grpc.ServerStream接口并加入帶有修改后元數(shù)據(jù)的context。以下是一個(gè)簡單的實(shí)現(xiàn):

type WrappedStream struct {
  grpc.ServerStream
  ctx context.Context
}

func (s *WrappedStream) Context() context.Context {
  return s.ctx
}

func ExampleStreamInterceptor(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
  md, ok := metadata.FromIncomingContext(ss.Context())
  md.append("new-key", "new-value")

  ctx := metadata.NewIncomingContext(ss.Context(), md)

  return handler(srv, &WrappedStream{ss, ctx})
}

流調(diào)用攔截器讀取響應(yīng)元數(shù)據(jù)

與在一元調(diào)用攔截器中相同,若需要在流調(diào)用攔截器中讀取響應(yīng)的元數(shù)據(jù),我們可以實(shí)現(xiàn)grpc.ServerStream接口,并在其中保存元數(shù)據(jù)的副本。例如我們可以在上節(jié)的WrappedStream的基礎(chǔ)上,對(duì)其進(jìn)行一定修改:

type WrappedStream struct {
  grpc.ServerStream

  header  metadata.MD
  trailer metadata.MD
}

func (s *WrappedStream) SendHeader(md metadata.MD) error {
  if err := s.ServerStream.SendHeader(md); err != nil {
    return err
  }

  s.header = md

  return nil
}

// 繼續(xù)實(shí)現(xiàn)SetHeader、SetTrailer等方法

func ExampleStreamInterceptor(srv any, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
  stream := &WrappedStream{ServerStream: ss}
  err := handler(srv, stream)

  // 通過stream.header、stream.trailer讀取響應(yīng)元數(shù)據(jù)

  return err
}

以上就是Golang處理gRPC請(qǐng)求/響應(yīng)元數(shù)據(jù)的示例代碼的詳細(xì)內(nèi)容,更多關(guān)于Golang處理gRPC請(qǐng)求/響應(yīng)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • golang gin 監(jiān)聽rabbitmq隊(duì)列無限消費(fèi)的案例代碼

    golang gin 監(jiān)聽rabbitmq隊(duì)列無限消費(fèi)的案例代碼

    這篇文章主要介紹了golang gin 監(jiān)聽rabbitmq隊(duì)列無限消費(fèi),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-12-12
  • go語言中線程池的實(shí)現(xiàn)

    go語言中線程池的實(shí)現(xiàn)

    Go語言中并沒有直接類似 Java 線程池的內(nèi)建概念,主要通過goroutine和channel來實(shí)現(xiàn)并發(fā)處理,本文主要介紹了go語言中線程池的實(shí)現(xiàn),具有一定的參考價(jià)值,感興趣的可以了解一下
    2025-04-04
  • Golang中下劃線(_)的不錯(cuò)用法分享

    Golang中下劃線(_)的不錯(cuò)用法分享

    golang中的下劃線表示忽略變量的意思,也沒有產(chǎn)生新的變量,但是后面的表達(dá)式依然會(huì)被執(zhí)行,本文為大家整理了golang中下劃線的一些不錯(cuò)的用法,需要的可以參考下
    2023-05-05
  • Go正則表達(dá)式匹配字符串,替換字符串方式

    Go正則表達(dá)式匹配字符串,替換字符串方式

    介紹了Go語言中使用正則表達(dá)式進(jìn)行字符串匹配和替換的方法,包括匹配單個(gè)子字符串和所有子字符串,個(gè)人經(jīng)驗(yàn)分享,旨在為讀者提供實(shí)用的編程技巧,并鼓勵(lì)大家支持腳本之家
    2025-02-02
  • Go語言庫系列之dotsql詳解

    Go語言庫系列之dotsql詳解

    這篇文章主要介紹了Go語言庫系列之dotsql的相關(guān)知識(shí),本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-04-04
  • Go語言interface詳解

    Go語言interface詳解

    這篇文章主要介紹了Go語言interface詳解,本文講解了什么是interface、interface類型、interface值、空interface、interface函數(shù)參數(shù)等內(nèi)容,需要的朋友可以參考下
    2014-10-10
  • 源碼剖析Golang中map擴(kuò)容底層的實(shí)現(xiàn)

    源碼剖析Golang中map擴(kuò)容底層的實(shí)現(xiàn)

    之前的文章詳細(xì)介紹過Go切片和map的基本使用,以及切片的擴(kuò)容機(jī)制。本文針對(duì)map的擴(kuò)容,會(huì)從源碼的角度全面的剖析一下map擴(kuò)容的底層實(shí)現(xiàn),需要的可以參考一下
    2023-03-03
  • go cron定時(shí)任務(wù)的基本使用講解

    go cron定時(shí)任務(wù)的基本使用講解

    這篇文章主要為大家介紹了gocron定時(shí)任務(wù)的基本使用講解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-06-06
  • golang goquery selector選擇器使用示例大全

    golang goquery selector選擇器使用示例大全

    這篇文章主要為大家介紹了golang goquery selector選擇器使用示例大全,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-09-09
  • go項(xiàng)目實(shí)現(xiàn)mysql接入及web?api的操作方法

    go項(xiàng)目實(shí)現(xiàn)mysql接入及web?api的操作方法

    這篇文章主要介紹了go項(xiàng)目實(shí)現(xiàn)mysql接入以及web api,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2023-08-08

最新評(píng)論