Golang gRPC學習(05): retry重試

什麼是重試

如果服務出現了錯誤,主要是網絡,服務器出現了短暫異常的時候,該怎麼辦?
我們都會人工或者自動的重新連接服務試試,看服務是否恢復可用了。

這種重新進行連接服務的一種方式就是重試。如果是在微服務裏,應該屬於微服務治理的範疇。
重試是處理網絡服務出現暫時不可用的一種方法。

怎麼進行重試

第一:你要能知道網絡出現了錯誤。
怎麼才能知道網絡出現了錯誤呢?
一種是你主動探測網絡是否可用,比如說健康檢查;一種是根據定義的錯誤碼來進行重試。比如http的一些錯誤碼。

第二:根據上面的探測或者錯誤碼決定是否重試。
因爲不是所有的錯誤都要進行重試,我們要根據具體情況來做決定是否重試。

第三:重試的策略
就是怎麼進行重試。
在gRPC的這份設計中,主要有2中重試策略:

  1. Retry Policy,出錯時立即重試

  2. Hedging Policy,定時發送併發的多個請求,根據請求的響應情況決定是否發送下一個同樣的請求,還是返回(好像該策略目前未實現)

  3. 在Retry和Heading基礎上的限流

客戶端的重試策略和限流策略都是用一個配置文件配置 - service config,這個配置文件用proto定義了一些字段和格式,文件在 grpc/service_config/service_config.proto 中,它最終會解析爲一個json格式,proto->json 規則文檔

一些Service Config文檔例子:doc/service_config

例子

參考官方的例子把我們前面的hello word程序用retry策略改寫下。

客戶端

主要是要在 Dial() 建立連接的這個函數里加一個參數 grpc.WithDefaultServiceConfig() ,這個就是配置重試策略的參數。

conn, err := grpc.Dial(Address, grpc.WithInsecure(),grpc.WithDefaultServiceConfig(retryPolicy))

裏面的參數 retryPolicy 是一個json的字符串,這個就是service config:

retryPolicy = `{
        "methodConfig":[{
           "name":[{"service": "grpc-tutorial.05retry.hello.hello"}],
           "waitForReady": true,
           "retryPolicy": {
                "MaxAttempts": 4,
                "InitialBakckoff": ".01s",
                "MaxBackoff": ".01s",
                "BackoffMultiplier": 1.0,
                 "RetryableStatusCodes": ["UNAVAILABLE"]
            }
    }]}`
  • MaxAttempts:
    最多請求次數。這裏設置爲4,一次原始請求,三次重試請求。

MaxAttempts必須是大於1的整數,對於大於5的值會被視爲5。

  • InitialBakckoff, BackoffMultiplier, MaxBackoff
    這3個參數要結合看, 意思是在進行下一次重試請求前,會計算需要等待的時間,計算公式爲:
    • 第一次重試間隔是 random(0, InitialBakckoff)
    • 第 n 次的重試間隔爲 random(0, min( InitialBakckoff*BackoffMultiplier*(n-1) , MaxBackoff ))

InitialBakckoff 和 MaxBackoff 必須指定,並且必須具有大於0。
BackoffMultiplier 必須指定,並且大於零。

  • RetryableStatusCodes
    會根據這個 RetryableStatusCodes 來判斷是否進行重試。這裏設置爲 UNAVAILABLE,會根據這個狀態來進行重試。

retryableStatusCodes 必須制定爲狀態碼的數據,不能爲空,並且沒有狀態碼必須是有效的 gPRC 狀態碼,可以是整數形式,並且不區分大小寫 ([14], [“UNAVAILABLE”], [“unavailable”)

服務端

在服務端我要製造重試的情況出來,主要是 failRequest() 這個函數,改寫一下程序:

type failServer struct {
    pb.UnimplementedHelloServer
    mu sync.Mutex

    reqNum uint
    reqMax uint
}

func (f *failServer) failRequest() error {
    f.mu.Lock()
    defer f.mu.Unlock()
    f.reqNum++
    if (f.reqMax > 0) && (f.reqNum % f.reqMax == 0) {
        return nil
    }
    return status.Errorf(codes.Unavailable, "failRequest: failing it")
}

然後在main函數初始化一下這個failServer struct:

failService := &failServer{
        reqNum: 0,
        reqMax: 4,
}

完整的例子在:這裏github

運行看看:
先運行服務端:go run server/main.go

在運行客戶端:GRPC_GO_RETRY=on go run client/main.go

注意:

運行客戶端一定要在環境變量里加上 GRPC_GO_RETRY=on

可是報錯了:
sayhello err: rpc error: code = Unavailable desc = failRequest: failing it
exit status 1

而且服務端也只打印了一條信息:
request failed num: 1

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章