grpc-go 連接backoff協議


當我們連接到一個失敗的后端時,通常希望不要立即重試(以避免泛濫的網絡或服務器的請求),而是做某種形式的指數backoff。

我們有幾個參數:

  1. INITIAL_BACKOFF (第一次失敗重試前后需等待多久)
  2. MULTIPLIER (在失敗的重試后乘以的倍數)
  3. JITTER (隨機抖動因子).
  4. MAX_BACKOFF (backoff上限)
  5. MIN_CONNECT_TIMEOUT (最短重試間隔)

建議backoff算法

以指數形式返回連接嘗試的起始時間,達到MAX_BACKOFF的極限,並帶有抖動。

1
2
3
4
5
6
7
ConnectWithBackoff()
current_backoff = INITIAL_BACKOFF
current_deadline = now() + INITIAL_BACKOFF
while (TryConnect(Max(current_deadline, now() + MIN_CONNECT_TIMEOUT))!= SUCCESS)
SleepUntil(current_deadline)
current_backoff = Min(current_backoff * MULTIPLIER, MAX_BACKOFF)
current_deadline = now() + current_backoff + UniformRandom(-JITTER * current_backoff, JITTER * current_backoff)

 

參數默認值MIN_CONNECT_TIMEOUT=20sec INITIAL_BACKOFF=1sec MULTIPLIER=1.6 MAX_BACKOFF=120sec JITTER=0.2

根據的確切的關注點實現(例如最小化手機的喚醒次數)可能希望使用不同的算法,特別是不同的抖動邏輯。

備用的實現必須確保連接退避在同一時間開始分散,並且不得比上述算法更頻繁地嘗試連接。

重置backoff

backoff應在某個時間點重置為INITIAL_BACKOFF,以便重新連接行為是一致的,不管連接的是新開始的還是先前斷開的連接。

當接收到SETTINGS幀時重置backoff,在那個時候,我們確定這個連接被服務器已經接受了。


grpc-go

源碼位於google.golang.org/grpc/backoff,代碼不多,直接在代碼上分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57

import (
"math/rand"
"time"
)

// 對應上面的默認值但是沒有實現MIN_CONNECT_TIMEOUT參數
var DefaultBackoffConfig = BackoffConfig{
MaxDelay: 120 * time.Second,
baseDelay: 1.0 * time.Second,
factor: 1.6,
jitter: 0.2,
}

// backoffStrategy是backoff算法的接口
type backoffStrategy interface {
// 通過重試次數返回在下一次重試之前等待的時間量
backoff(retries int) time.Duration
}

type BackoffConfig struct {
MaxDelay time.Duration
baseDelay time.Duration
factor float64
jitter float64
}

func setDefaults(bc *BackoffConfig) {
md := bc.MaxDelay
*bc = DefaultBackoffConfig

if md > 0 {
bc.MaxDelay = md
}
}

// backoff算法的基礎實現
func (bc BackoffConfig) backoff(retries int) time.Duration {
if retries == 0 {
return bc.baseDelay
}
backoff, max := float64(bc.baseDelay), float64(bc.MaxDelay)
for backoff < max && retries > 0 {
backoff *= bc.factor
retries--
}
if backoff > max {
backoff = max
}
// Randomize backoff delays so that if a cluster of requests start at
// the same time, they won't operate in lockstep.
backoff *= 1 + bc.jitter*(rand.Float64()*2-1)
if backoff < 0 {
return 0
}
return time.Duration(backoff)
}

 

如果默認的backoff算法不滿足需求的時候,還可以自定義backoff算法,通過實現backoffStrategy接口。

1
2
3
4
5
6
7
func withBackoff(bs backoffStrategy) DialOption {
return func(o *dialOptions) {
o.bs = bs
}
}

grpc.Dial(addr, grpc.withBackoff(mybackoff))


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM