golang在GRPC中設置client的超時時間
超時
建立連接
主要就2函數Dail和DialContext。
// Dial creates a client connection to the given target. func Dial(target string, opts ...DialOption) (*ClientConn, error) { return DialContext(context.Background(), target, opts...) }
func DialContext(ctx context.Context, target string, opts ...DialOption) (conn *ClientConn, err error){...}
DialContext 太長瞭不帖瞭.看Dial實際上也是調用DialContext來實現的.如果你想在建立連接的時候使用超時控制.就使用
DialContext傳入一個Timeout的context,就像下面的例子
ctx1, cel := context.WithTimeout(context.Background(), time.Second*3) defer cel() conn, err := grpc.DialContext(ctx1, address, grpc.WithBlock(), grpc.WithInsecure())
另外調用Dial建立連接默認隻是返回一個ClientConn的指針,相當於new瞭一個ClientConn 把指針返回給你。並不是一定要建立真實的h2連接.至於真實的連接建立實際上是一個異步的過程。
當然瞭如果你想等真實的鏈接完全建立再返回ClientConn可以通過WithBlock傳入Options來實現,當然瞭這樣的話鏈接如果建立不成功就會一直阻塞直到Contex超時。
真正的建立鏈接的代碼後面介紹重試的時候會再詳細介紹。
調用超時
這個比較簡單
ctx, cancel := context.WithTimeout(context.TODO(), time.Second*3) defer cancel() r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name})
如上代碼傳入一個timeout context就可以。
Server
type SearchService struct{} func (s *SearchService) Search(ctx context.Context, r *pb.SearchRequest) (*pb.SearchResponse, error) { for i := 0; i < 5; i++ { if ctx.Err() == context.Canceled { return nil, status.Errorf(codes.Canceled, "SearchService.Search canceled") } time.Sleep(1 * time.Second) } return &pb.SearchResponse{Response: r.GetRequest() + " Server"}, nil } func main() { ... }
而在 Server 端,由於 Client 已經設置瞭截止時間。Server 勢必要去檢測它
否則如果 Client 已經結束掉瞭,Server 還傻傻的在那執行,這對資源是一種極大的浪費
因此在這裡需要用 ctx.Err() == context.Canceled 進行判斷,為瞭模擬場景我們加瞭循環和睡眠 ?
驗證
重新啟動 server.go 和 client.go,得到結果:
$ go run client.go 2018/10/06 17:45:55 client.Search err: deadline exit status 1
總結
本章節比較簡單,你需要知道以下知識點:
怎麼設置 Deadlines
為什麼要設置 Deadlines
你要清楚地明白到,gRPC Deadlines 是很重要的,否則這小小的功能點就會要瞭你生產的命。
補充:golang使用grpc超時控制和對沖策略
超時控制
grcp超時控制設置在客戶端調用服務時,如果設定瞭超時時間,客戶端會立即返回超時。超時控制一般有三個因素:鏈路超時:上有調用端通過協議字段把自己允許的超時時間傳給當前服務,表示在該時間內返回數據,超時返回已無意義。流程如下圖A調用B的總超時情況。
消息超時:服務端收到請求消息到返回響應數據的最長消息處理時間。下圖的B內部的當前請求整體超時時間。調用超時:當前服務調用下遊服務設置的每一個rpc請求的超時時間。如下圖B調用C的單個超時時間。通常一次請求會連續調用多次rpc,這個調用超時控制的是每個rpc的獨立超時時間。
發起rpc調用請求時,需要計算此次rpc調用的超時時間。真正生效的超時時間是通過以上三個因素實時計算的最小值,計算過程
如下:
1、首先計算鏈路超時和消息超時的最小值,如鏈路超時2s,消息超時1s,則當前消息的最長處理時間為1s。
2、發起rpc調用時,再次計算當前消息最長處理時間和單個超時時間的最小值,比如:上圖的B->C設置的單個超時時間為5s,則實際上B調用C的真實超時仍然是1s,其實隻要超時時間大於當前最長處理時間都是無效的,都會取最小值。再比如B->C單個超時時間為500ms,這種情況B調用C的真實超時即為500ms,此時500ms這個值也會通過協議字段傳給C,在服務端C的視角來看就是他的鏈路超時時間。鏈路超時時間會在整個rpc調用鏈上一直傳遞下去,並逐漸減少,直至為0,這樣避免出現死循環調用的問題。
3、因為每一次rpc調用都會實際消耗一部分時間,所以當前消息最長處理時間需要實時計算剩餘時間,比如上面B調用C真實耗時200ms,此時最長處理時間就隻剩下800ms瞭。此時發起第二次rpc調用時,則需要計算此時剩餘的消息超時時間和單個調用時間的最小值。如上圖的B->D設置的單個超時時間為1s,則實際生效的超時時間仍然為800ms。鏈路超時設置:golang的context.Context根據協議裡面的timeout字段和框架配置的timeout字段。設置好當前請求的最長處理時間,然後交給用戶使用,並在處理函數結束時會立馬cancel掉當前context。所以在創建新的goroutine時,需要重新設定新的context。
對沖策略
對沖策略不是被動的等待上一次請求超時或者失敗,在對沖延時時間內(或小於超時時間)如果沒有收到回復的包就會再觸發一個請求。
與重試策略不同的是同一時間內in-fliaght可能有多個,當接收到第一回復時,其他的回復會被忽略。
一、重試策略:
對失敗的請求,進行重新請求。
由圖中可以看出,client一共進行瞭三次請求,前兩次均失敗,並且在重新請求時都會隨機避段時間,防止請求毛刺,第三次請求成功,返回給應用層。對於每次嘗試,我們都會盡可能地將請求發往不同的節點。
通常重試策略有三種配置:
1、失敗重新請求的最大次數,達到最大次數仍然失敗,不再進行重試;
2、退避時間:退避時間取的是 random(0, delay);
3、可重試錯誤碼:設置可錯誤碼,對於不可重試的,立即停止重試並將錯誤返回應用層。
二、對沖策略
上圖中client一共進行瞭4次,橙、藍、綠、紫
橙色是第一次嘗試。在由 client 發起後,server2 很快便收到瞭。但是 server2 的因為網絡等問題,直到綠色請求成功,並返回給應用層後,它的正確回包才姍姍來遲。盡管它成功瞭,但我們必須丟棄它,因為我們已經將另一個成功的回包返回給應用層瞭。
藍色是第二次嘗試。因為橙色請求在對沖時延(hedging delay)後還沒有回包,因此我們發起瞭一次新的嘗試。這次嘗試選擇瞭 server1(我們會盡可能地為每次嘗試選擇不同的節點)。藍色嘗試的回包比較快,在對沖時延之前便返回瞭。但是卻失敗瞭。我們立刻發起瞭新一次嘗試。
綠色是第三次嘗試。盡管它的回包可能有點慢(超過瞭對沖時延,因此又觸發瞭一次新的嘗試),但是它成功瞭!一旦我們收到第一個成功的回包,便立刻將它返回給瞭應用層。
紫色是第四次嘗試。剛發起後,我們便收到瞭綠色成功的回包。對紫色來說,它可能處於很多狀態:請求還在 client gRPC 內,這時,我們有機會取消它;請求已經進入瞭 client 的內核或者已經由網卡發出,無論如何,我們已經沒有機會取消它瞭。紫色請求上的 ✘ 表示我們會盡可能地取消紫色請求。註意,即使紫色請求最終成功地到達瞭 server2,它的回包也會像橙色一樣被丟棄。
由以上可知對沖策略更像是添加瞭等待時間的重試,但是他沒有退避機制,一旦收到錯誤的包,立刻發起重試。這種對於需要解決長尾問題時推薦使用,一般情況建議使用重試策略。
對沖策略一般有三種配置
1、對沖延時:在對對沖時延內沒有收到回包時便會立刻發起新的嘗試;
2、最大請求次數:一旦耗盡,便等待並返回最後一個回包,無論它是否成功或失敗;
3、非致命錯誤:返回致命錯誤會立刻中止對沖,等待並返回最後一個回包,無論它是否成功或失敗。返回非致命錯誤會立刻觸發一次新的嘗試(對沖時延計時器會被重置)。
以上為個人經驗,希望能給大傢一個參考,也希望大傢多多支持WalkonNet。如有錯誤或未考慮完全的地方,望不吝賜教。
推薦閱讀:
- Go gRPC超時控制Deadlines用法詳解
- go語言中http超時引發的事故解決
- Go gRPC教程實現Simple RPC
- golang DNS服務器的簡單實現操作
- 基於微服務框架go-micro開發gRPC應用程序