在之前的go语言的速率限制这篇文章里,我们尝试了普通的速率限制,和脉冲型速率限制。其中,脉冲型速率限制是放开了限制,里面有3个请求是一次性到达,然后再按照200ms的速度限制的,之前的代码如下所示:
package main import "fmt" import "time" func main() { requests := make(chan int, 5) for i := 1; i <= 5; i++ { requests <- i } close(requests) limiter := time.Tick(time.Millisecond * 200) for req := range requests { <-limiter fmt.Println("request", req, time.Now()) } burstyLimiter := make(chan time.Time, 3) for i := 0; i < 3; i++ { burstyLimiter <- time.Now() } go func() { for t := range time.Tick(time.Millisecond * 200) { burstyLimiter <- t } }() burstyRequests := make(chan int, 5) for i := 1; i <= 5; i++ { burstyRequests <- i } close(burstyRequests) for req := range burstyRequests { <-burstyLimiter fmt.Println("request", req, time.Now()) } }
最终的输出是下面这样,可以看到下面那段输出的前三次输出,时间几乎没差,这是一次脉冲型速率限制:
request 1 2018-04-17 12:57:02.823975218 +0800 CST m=+0.205374957 request 2 2018-04-17 12:57:03.024067833 +0800 CST m=+0.405476106 request 3 2018-04-17 12:57:03.220187209 +0800 CST m=+0.601603847 request 4 2018-04-17 12:57:03.420175881 +0800 CST m=+0.801601050 request 5 2018-04-17 12:57:03.622105704 +0800 CST m=+1.003539485 request 1 2018-04-17 12:57:03.622191244 +0800 CST m=+1.003625029 request 2 2018-04-17 12:57:03.622210962 +0800 CST m=+1.003644748 request 3 2018-04-17 12:57:03.622223153 +0800 CST m=+1.003656939 request 4 2018-04-17 12:57:03.82356235 +0800 CST m=+1.205004724 request 5 2018-04-17 12:57:04.024178896 +0800 CST m=+1.405629826
那我们如果想实现另一种脉冲型速率限制怎么办,就是一开始,让速度变慢,然后再正常请求的,经过思考,代码改造如下:
package main import "fmt" import "time" func main() { k := 0 requests := make(chan int, 5) for i := 1; i <= 5; i++ { requests <- i } close(requests) limiter := time.Tick(time.Millisecond * 200) slow := time.Tick(time.Millisecond * 400) for req := range requests { if k < 3 { <-slow k++ } <-limiter fmt.Println("request", req, time.Now()) } }
运行结果如下,可以看到,前三次间隔是400ms,第四次又还原回来间隔200ms:
request 1 2018-04-17 17:04:50.9986303 +0800 CST m=+0.405591505 request 2 2018-04-17 17:04:51.395919457 +0800 CST m=+0.802875205 request 3 2018-04-17 17:04:51.794626945 +0800 CST m=+1.201577217 request 4 2018-04-17 17:04:51.993709916 +0800 CST m=+1.400657453 request 5 2018-04-17 17:04:52.196450634 +0800 CST m=+1.603395386
我来解释下代码逻辑。为了实现需求,我在前面又设置了一个打点器,然后增加判断,如果k小于3,就从打点器中取值,然后k自增。这样的话保证了前三次取值慢,后面的取值快。
这时候有人会问了,那例子中是总共两个打点器,一个是400ms,一个是200ms,为什么前三次不是400+200总共600ms,而是间隔只有400ms呢?
我来解释一下,因为代码运行到<-slow时候是阻塞状态,通道内没有值,是需要打点器每隔400ms把值存入slow中,才能继续运行的。而<-limiter按理说也是阻塞状态,也需要打点器传值的。但是别忘了,limiter是200ms,在slow传值之前,limiter就已经存入值了。所以在if判断语句结束以后,limiter不用等待传值,直接取就行了。这样的话,在if条件执行完后,<-limiter执行是瞬间完成的,不用等待200ms的。
以上就是go语言中速率限制的一些思考和改造,在实际工作中,应该还有更加完美的解决方案,期待将来的改进。