目前有三類重試:超時(shí)重試、Backup Request,建連失敗重試(默認(rèn))。其中建連失敗是網(wǎng)絡(luò)層面問題,由于請求未發(fā)出,框架會默認(rèn)重試。 本文檔介紹前兩類重試的使用:
因?yàn)楹芏嗟臉I(yè)務(wù)請求不具有冪等性,這兩類重試不會作為默認(rèn)策略。
超時(shí)重試和 Backup Request 策略只能配置其中之一。
配置項(xiàng) | 默認(rèn)值 | 說明 | 限制 |
---|---|---|---|
MaxRetryTimes
|
2 | 最大重試次數(shù),不包含首次請求。如果配置為 0 表示停止重試。 | 合法值:[0-5] |
MaxDurationMS
|
0 | 累計(jì)最大耗時(shí),包括首次失敗請求和重試請求耗時(shí),如果耗時(shí)達(dá)到了限制的時(shí)間則停止后續(xù)的重試。0 表示無限制。注意:如果配置,該配置項(xiàng)必須大于請求超時(shí)時(shí)間。 | |
EERThreshold
|
10% | 重試熔斷錯(cuò)誤率閾值, 方法級別請求錯(cuò)誤率超過閾值則停止重試。 | 合法值:(0-30%] |
ChainStop
|
- | 鏈路中止, 默認(rèn)啟用。如果上游請求是重試請求,超時(shí)后不會重試。 | >= v0.0.5 后作為默認(rèn)策略 |
DDLStop
|
false | 鏈路超時(shí)中止,該策略是從鏈路的超時(shí)時(shí)間判斷是否需要重試。注意,Kitex 未內(nèi)置該實(shí)現(xiàn),需通過 retry.RegisterDDLStop(ddlStopFunc) 注冊 DDL func,結(jié)合鏈路超時(shí)判斷,實(shí)現(xiàn)上建議基于上游的發(fā)起調(diào)用的時(shí)間戳和超時(shí)時(shí)間判斷。?? | |
BackOff
|
None | 重試等待策略,默認(rèn)立即重試(NoneBackOff )??蛇x:固定時(shí)長退避 (FixedBackOff )、隨機(jī)時(shí)長退避 (RandomBackOff )。 |
|
RetrySameNode
|
false | 框架默認(rèn)選擇其他節(jié)點(diǎn)重試,若需要同節(jié)點(diǎn)重試,可配置為 true。 |
配置項(xiàng) | 默認(rèn)值 | 說明 | 限制 |
---|---|---|---|
RetryDelayMS
|
- | Backup Request 的等待時(shí)間,若該時(shí)間內(nèi)若請求未返回,會發(fā)送新的請求。必須手動(dòng)配置,建議參考 TP99。 | |
MaxRetryTimes
|
1 | 最大重試次數(shù),不包含首次請求。 如果配置為 0 表示停止重試。 | 合法值:[0-2] |
EERThreshold
|
10% | 重試熔斷錯(cuò)誤率閾值,方法級別請求錯(cuò)誤率超過閾值則停止重試。 | 合法值:(0-30%] |
ChainStop
|
- | 鏈路中止, 默認(rèn)啟用。如果上游請求是重試請求,不會發(fā)送 Backup Request。 | >= v0.0.5 后作為默認(rèn)策略 |
RetrySameNode
|
false | 框架默認(rèn)選擇其他節(jié)點(diǎn)重試,若需要同節(jié)點(diǎn)重試,可配置為 true |
注意:若通過代碼配置開啟重試,動(dòng)態(tài)配置 (見 3.3) 則無法生效。
// import "github.com/cloudwego/kitex/pkg/retry"
fp := retry.NewFailurePolicy()
fp.WithMaxRetryTimes(3) // 配置最多重試3次
xxxCli := xxxservice.NewClient("destServiceName", client.WithFailureRetry(fp))
fp := retry.NewFailurePolicy()
// 重試次數(shù), 默認(rèn)2,不包含首次請求
fp.WithMaxRetryTimes(xxx)
// 總耗時(shí),包括首次失敗請求和重試請求耗時(shí)達(dá)到了限制的duration,則停止后續(xù)的重試。
fp.WithMaxDurationMS(xxx)
// 關(guān)閉鏈路中止
fp.DisableChainRetryStop()
// 開啟DDL中止
fp.WithDDLStop()
// 退避策略,默認(rèn)無退避策略
fp.WithFixedBackOff(fixMS int) // 固定時(shí)長退避
fp.WithRandomBackOff(minMS int, maxMS int) // 隨機(jī)時(shí)長退避
// 開啟重試熔斷
fp.WithRetryBreaker(errRate float64)
// 同一節(jié)點(diǎn)重試
fp.WithRetrySameNode()
建議配置為 TP99,則 1% 請求會觸發(fā) Backup Request。
// 首次請求 xxx ms未返回,發(fā)起 backup 請求,并開啟鏈路中止
bp := retry.NewBackupPolicy(xxx)
xxxCli := xxxservice.NewClient("destServiceName", client.WithBackupRequest(bp))
bp := retry.NewBackupPolicy(xxx)
// 重試次數(shù), 默認(rèn)1,不包含首次請求
bp.WithMaxRetryTimes(xxx)
// 關(guān)閉鏈路中止
bp.DisableChainRetryStop()
// 開啟重試熔斷
bp.WithRetryBreaker(errRate float64)
// 同一節(jié)點(diǎn)重試
bp.WithRetrySameNode()
當(dāng)開啟了服務(wù)的熔斷配置可以復(fù)用熔斷的統(tǒng)計(jì)減少額外的 CPU 消耗,注意重試的熔斷閾值須低于服務(wù)的熔斷閾值,使用如下:
// 1. 初始化 kitex 內(nèi)置的 cbsuite
cbs := circuitbreak.NewCBSuite(circuitbreak.RPCInfo2Key)
// 2. 初始化 retryContainer,傳入ServiceControl和ServicePanel
retryC := retry.NewRetryContainerWithCB(cs.cbs.ServiceControl(), cs.cbs.ServicePanel())
var opts []client.Option
// 3. 配置 retryContainer
opts = append(opts, client.WithRetryContainer(retryC))
// 4. 配置 Service circuit breaker
opts = append(opts, client.WithMiddleware(cbs.ServiceCBMW()))
// 5. 初始化 Client, 傳入配置 option
cli, err := xxxservice.NewClient(targetService, opts...)
若需要結(jié)合遠(yuǎn)程配置,動(dòng)態(tài)開啟重試或運(yùn)行時(shí)調(diào)整策略,可以通過 retryContainer 的 NotifyPolicyChange 方法生效,目前 Kitex 開源版本暫未提供遠(yuǎn)程配置模塊,使用者可集成自己的配置中心。注意:若已通過代碼配置開啟,動(dòng)態(tài)配置則無法生效。 使用示例:
retryC := retry.NewRetryContainer()
// demo
// 1. define your change func
// 2. exec yourChangeFunc in your config module
yourChangeFunc := func(key string, oldData, newData interface{}) {
newConf := newData.(*retry.Policy)
method := parseMethod(key)
retryC.NotifyPolicyChange(method, policy)
}
// configure retryContainer
cli, err := xxxservice.NewClient(targetService, client.WithRetryContainer(retryC))
Kitex 對重試的請求在 rpcinfo 中記錄了重試次數(shù)和之前請求的耗時(shí),可以在Client側(cè)的 metric 或日志中根據(jù) retry tag 區(qū)分上報(bào)或輸出。獲取方式:
var retryCount string
var lastCosts string
toInfo := rpcinfo.GetRPCInfo(ctx).To()
if retryTag, ok := toInfo.Tag(rpcinfo.RetryTag); ok {
retryCount = retryTag
if lastCostTag, ok := toInfo.Tag(rpcinfo.RetryLastCostTag); ok {
lastCosts = lastCostTag
}
}
如果使用 TTHeader 作為傳輸協(xié)議,下游 handler 可以通過如下方式判斷當(dāng)前是否是重試請求,自行決定是否繼續(xù)處理。
retryReqCount, exist := metainfo.GetPersistentValue(ctx,retry.TransitKey)
比如 retryReqCount = 2,表示第二次重試請求(不包括首次請求),則采取業(yè)務(wù)降級策略返回部分或 mock 數(shù)據(jù)返回(非重試請求沒有該信息)。
Q: 框架默認(rèn)開啟鏈路中止,業(yè)務(wù)是否還有必要識別重試請求?
鏈路中止是指鏈路上的重試請求不會重試,比如 A->B->C,A 向 B 發(fā)送的是重試請求,如果 B->C 超時(shí)了或者配置了 Backup,則 B 不會再發(fā)送重試請求到 C。如果業(yè)務(wù)自行識別重試請求,可以直接決定是否繼續(xù)請求到 C。簡言之鏈路中止避免了 B 向 C 發(fā)送重試請求導(dǎo)致重試放大,業(yè)務(wù)自己控制可以完全避免 B 到 C 的請求。
更多建議: