time.Duration的理解

假设,我们指定的生成令牌的速率是每秒产生965个令牌,那么每生成一个令牌的间隔是多少呢?大约是每1.0362毫秒产生一个令牌。那么如果是在100毫秒这段时间会产生多少个令牌呢?大约103.62个令牌。

好了,既然是float64,那么在计算给定时间段内产生的tokens总数时就会有精度问题。我们来看看time/rate包中限速器的某段代码

func (limit Limit) tokensFromDuration(d time.Duration) float64 {
    sec := float64(d/time.Second) * float64(limit)
    nsec := float64(d%time.Second) * float64(limit)
    return sec + nsec/1e9
}

//疑问,为什么要按照秒来计算,直接计算每纳秒产生多少个,然后计算过了多少纳秒不就好了?

fmt.Println((float64(3) / float64(time.Second)) * 123123) //0.000369369

time.Duration 是 int64 的别名,代表纳秒。分别求出秒的整数部分和小数部分,进行相乘后再相加,这样可以得到最精确的精度。

type Duration int64

const (
    Nanosecond  Duration = 1
    Microsecond          = 1000 * Nanosecond
    Millisecond          = 1000 * Microsecond
    Second               = 1000 * Millisecond
    Minute               = 60 * Second
    Hour                 = 60 * Minute
)


start := time.Now()
fmt.Printf("t:%d\n", time.Now().Sub(start)) //这里打印出来的是纳秒

time.Sleep(1*time.Second) //sleep函数的参数值其实是int64,time.Second帮我们封装了秒到纳秒的转换

限速器的实现及分类

漏桶限速器:https://github.com/uber-go/ratelimit
令牌桶限速器:golang/org/x/time/rate

漏桶强调的是恒定速率,比如,一秒漏10个。每次处理请求之前,都判断上次处理的时间,如果小于100ms,则不通过,大于等于100ms,则通过,并更新时间。及时一个小时没有请求了,突然来了两个请求,他们之间也要间隔100ms。一般漏洞都会有一个容器,把请求存起来,然后按照固定的速率处理。

令牌桶则可以累积,如果容量100,在1ms 内来了100个请求,都能通过。