我们使用单个IP在10ms内发并发送了6个请求,只有1个成功,剩下的5个都被拒绝。我们设置的速度是2r/s,为什么只有1个成功呢,是不是Nginx限制错了?当然不是,是因为Nginx的限流统计是基于毫秒的,我们设置的速度是2r/s,转换一下就是500ms内单个IP只允许通过1个请求,从501ms开始才允许通过第二个请求。
实验2——burst允许缓存处理突发请求实验1我们看到,我们短时间内发送了大量请求,Nginx按照毫秒级精度统计,超出限制的请求直接拒绝。这在实际场景中未免过于苛刻,真实网络环境中请求到来不是匀速的,很可能有请求“突发”的情况,也就是“一股子一股子”的。Nginx考虑到了这种情况,可以通过burst关键字开启对突发请求的缓存处理,而不是直接拒绝。
来看我们的配置:
... limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s; server { location / { limit_req zone=mylimit burst=4; } } ...我们加入了burst=4,意思是每个key(此处是每个IP)最多允许4个突发请求的到来。如果单个IP在10ms内发送6个请求,结果会怎样呢?
# 单个IP 10ms内发送6个请求,设置burst send 6 requests in parallel, time cost: 2 ms HTTP/1.1 200 OK HTTP/1.1 503 Service Temporarily Unavailable HTTP/1.1 200 OK HTTP/1.1 200 OK HTTP/1.1 200 OK HTTP/1.1 200 OK end, total time cost: 2437 ms相比实验1成功数增加了4个,这个我们设置的burst数目是一致的。具体处理流程是:1个请求被立即处理,4个请求被放到burst队列里,另外一个请求被拒绝。通过burst参数,我们使得Nginx限流具备了缓存处理突发流量的能力。
但是请注意:burst的作用是让多余的请求可以先放到队列里,慢慢处理。如果不加nodelay参数,队列里的请求不会立即处理,而是按照rate设置的速度,以毫秒级精确的速度慢慢处理。
实验3——nodelay降低排队时间实验2中我们看到,通过设置burst参数,我们可以允许Nginx缓存处理一定程度的突发,多余的请求可以先放到队列里,慢慢处理,这起到了平滑流量的作用。但是如果队列设置的比较大,请求排队的时间就会比较长,用户角度看来就是RT变长了,这对用户很不友好。有什么解决办法呢?nodelay参数允许请求在排队的时候就立即被处理,也就是说只要请求能够进入burst队列,就会立即被后台worker处理,请注意,这意味着burst设置了nodelay时,系统瞬间的QPS可能会超过rate设置的阈值。nodelay参数要跟burst一起使用才有作用。
延续实验2的配置,我们加入nodelay选项:
... limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s; server { location / { limit_req zone=mylimit burst=4 nodelay; } } ...单个IP 10ms内并发发送6个请求,结果如下:
# 单个IP 10ms内发送6个请求 实验3, 设置burst和nodelay | 实验2, 只设置burst send 6 requests, time cost: 4 ms | time cost: 2 ms HTTP/1.1 200 OK | HTTP/1.1 200 OK HTTP/1.1 200 OK | HTTP/1.1 503 ... HTTP/1.1 200 OK | HTTP/1.1 200 OK HTTP/1.1 200 OK | HTTP/1.1 200 OK HTTP/1.1 503 ... | HTTP/1.1 200 OK HTTP/1.1 200 OK | HTTP/1.1 200 OK total time cost: 465 ms | total time cost: 2437 ms跟实验2相比,请求成功率没变化,但是总体耗时变短了。这怎么解释呢?实验2中,有4个请求被放到burst队列当中,工作进程每隔500ms(rate=2r/s)取一个请求进行处理,最后一个请求要排队2s才会被处理;实验3中,请求放入队列跟实验2是一样的,但不同的是,队列中的请求同时具有了被处理的资格,所以实验3中的5个请求可以说是同时开始被处理的,花费时间自然变短了。