-
Notifications
You must be signed in to change notification settings - Fork 2.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
limit req may be not strictly accurate under certain circumstances #855
Comments
excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000; maybe you can consider excess as the total request nums in the limit queue so, we can get as follows: nums in queue now = nums in queue before - nums can be processed in passed ms + this request
therefore, we do not need to consider "ms = (ngx_msec_int_t) (now - lr->last) will return 0" wish to be helpful |
@hongxiaolong 兄弟,才看到你的评论。 这个算法应该属于令牌桶算法,应该是存在误差的,假设以下场景: 在高并发请求的情况下,配置限速为10000,实际上可能达到20000。 这种可能后端业务系统就会被压死,引起一系列崩溃。 我的意思是可以通过限制每ms令牌桶最大容量,而不是每秒令牌桶最大容量来解决这个问题。 阿里的兄弟也来讨论下这个问题吧 |
可能你对令牌桶算法的理解有一点偏差,摘一段维基百科解释如下: 令牌桶: 1、每秒将 r 个令牌放入令牌桶,或者说,每过 1/r 秒桶中增加一个令牌; 而且,NGINX中的限流算法更准确地说其实是漏桶和令牌桶算法思想的结合:
在没有设置"burst"时,用漏桶可能更易理解; 在设置"burst"允许一定量的突发时,令牌桶的思想可能更贴切。 还是直接上代码解释一下最开始的回答:
剩下的很容易理解,当前时刻队列请求数即左值excess:
不知这样解答是否可以有帮助 如有兴趣,后续我也可以汇总一下NGINX官方的限流模块及其算法,到时候可以贴出来一起讨论 多谢 |
@hongxiaolong 实际上允许的连接应该是rate+burst。刚好实现了限流,和分析的一致。 |
好的,你的问题应该已完结, 我关闭了该issue,如果有问题可以再打开 |
@hongxiaolong 我觉得,如果rata / 1000 > limit->burst,应该改为如下: 或者在检查配置的时候,如果配置rata > 1000,则burst 必须大于rate/1000 @hongxiaolong |
我们还是继续来分析一下这段代码,或者说是公式。 以你说的rate=10000r/s,burst=1为例子: 0ms: excess = 0 - 10000000 * 1 / 1000 + 1000 = -9000 可以看到,我们得到的excess为一个很大的负数,而且在这里我们已经假设ms这个值很小为1的情况,实际情况会更大。
上面这段代码,如果要条件成立,起码excess要超过0,这样的情况,起码 lr->excess这个队列中已经存在很多的请求,见
综上,各种条件其实还是满足限流算法的思想的: 首先,限流队列中已经有很多请求,且在这样的情况下,单位时间内还是超过了burst的上限,才会被限流。 wish to be helpful, thanks |
@hongxiaolong 这里的时间精度单位为ms,如果请求在1ms内全部过来,ms = (ngx_msec_int_t) (now - lr->last) = 0 上周四晚上我司做全链路压测,我专门搭了一套环境,配置rate=10000, burst=1, 压测情况下,放行的请求数为每秒1000左右,和我上面的分析一致,单我本来预期放行的请求数十10000左右。 @hongxiaolong |
@y123456yz 赞思路和测试 我觉得我们已经讨论了两个维度: 1、NGINX现在的限流算法是否合理; NGINX的限流算法代码分析后,现在的限流算法是合理的,它的配置项rate、burst都很好地支持着流量的限流,符合预期,而且NGINX限流默认的时间维度是毫秒级,也就是说rate的指标换算成毫秒级的数量后,NGINX可以很精确地进行限流。 2、NGINX现在的限流算法是否可以优化,或者说符合更大流量的需求场景; Linux的系统调用gettimeofday的时间维度是精确到微秒级的,NGINX是基于该系统调用来更新时间的:
可以看到,NGINX是在微秒级上再除以1000来得到其默认的时间维度毫秒级。 所以你的推算也是符合预期的,即在1毫秒内进来10000个请求,NGINX实际放过去的请求量是每毫秒burst=1,也就是每秒1000左右。 那么,如果我们把维度提升到微秒级,理论上NGINX可以提升限流性能上千倍,但是,我们来考虑另一个角度,我们限流的主要目的还是保持服务可用,NGINX的每个连接内存占用在1K左右,10万连接在1G左右,如果可以提升一千倍性能,短时间内连接数激增,内存占用会在百倍千倍量级去提升,现在的硬件条件估计无法满足,而且epoll在这种短时间内的性能下降估计也会很可观。 综上,一定程度上NGINX现有的限流算法会丧失一些精度,不知道当时NGINX作者在默认时间维度为毫秒时是否已经提前考虑到其它的综合情况,而且估计到了这个量级,可能一般公司更多会去注重总系统而不是单机的性能瓶颈,推荐你可以NGINX mailist里邮件问问,可以互相探讨学习~ 多谢 |
@hongxiaolong 我觉得取us作为时间精度的话,突发流量会压到us时间段内,系统应该会处理不过来,应该就是你的分析,作者估计也会考虑到这个。 另外: 就如上面我分析的,如配置rate=10000,burst=1,那么OK的请求数只能为1000左右,感觉这个是个bug。我觉得不调整时间精度,可以做如下修改,同样可以满足限速要求,并且每秒突发最多为burst: 法1: 法2: 如果可以解决,法2相对比较好。 抽空我发发邮件到nginx mailist,问下他们的想法。 |
回去看了一眼NGINX对这两个配置项变量的注释,如下:
感觉原作者可能想表达的是,在没有设置burst时rate在0.001r/s,如果设置了burst,那么burst优先生效,还是蛮符合漏桶的思想的。 当然,我感觉你的思路未尝也是一种不错的方案,我的如上阅读理解你可以参考,希望有所帮助。 多谢 |
@y123456yz 假如:双11零点瞬间,1ms内来了10000个请求,发行请求每秒1000请求,没有问题,因为漏桶和令牌的消耗是成比例的,为什么会预期是几十万请求? 可以在nginx官网Report a Bug里讨论,用你的github账号登录。 |
ngx_http_limit_req_lookup()
{
...............
ms = (ngx_msec_int_t) (now - lr->last);
excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
...........
}
if there have several connections(For example 10 connections ) in a millimeter, ms = (ngx_msec_int_t) (now - lr->last) will return 0, then excess +=1000, So excess is larger than the actual value , for example, 10000 requests in a millisecon.
I think the unit of max Token bucket should be ms.it will be More accurate
The text was updated successfully, but these errors were encountered: