grpc-go 链接backoff协议

当咱们链接到一个失败的后端时,一般但愿不要当即重试(以免泛滥的网络或服务器的请求),而是作某种形式的指数backoff。golang

咱们有几个参数:算法

  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服务器

根据的确切的关注点实现(例如最小化手机的唤醒次数)可能但愿使用不一样的算法,特别是不一样的抖动逻辑。网络

备用的实现必须确保链接退避在同一时间开始分散,而且不得比上述算法更频繁地尝试链接。dom

重置backoff

backoff应在某个时间点重置为INITIAL_BACKOFF,以便从新链接行为是一致的,无论链接的是新开始的仍是先前断开的链接。google

当接收到SETTINGS帧时重置backoff,在那个时候,咱们肯定这个链接被服务器已经接受了。spa


grpc-go

源码位于google.golang.org/grpc/backoff,代码很少,直接在代码上分析。code

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接口。orm

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))
相关文章
相关标签/搜索