_来源:https://www.cnblogs.com/bigli...作者:biglittleant_1,限流算法tokenbucket算法tokenbucket算法算法思想是:token以固定的速率生成并缓存在令牌桶中;当令牌桶满时,丢弃多余的令牌;请求消耗相同比例的令牌进行处理;当令牌不够时,请求被缓存。漏桶算法漏桶算法的思想是:水(请求)从上方倒入桶中,从桶底流出(待处理);来不及流出的水存储在桶中(缓冲),并以固定的速度流出;水桶满了(扔掉),水就溢出来了。该算法的核心是:缓存请求,匀速处理,直接丢弃多余的请求。与漏桶算法相比,令牌桶算法的不同之处在于它不仅有“桶”,还有队列。这个bucket是用来存放token的,queue是用来存放request的。在功能上,漏桶算法和令牌桶算法最明显的区别是是否允许处理突发流量(burst)。漏桶算法可以强制限制数据的实时传输(处理)速率,不做额外的处理;而令牌桶算法可以限制平均数据传输速率,同时允许一定程度的突发传输。Nginx请求速率限速模块采用漏桶算法,可以强制保证请求的实时处理速度不会超过设定的阈值。Nginx正式版有两个限制IP连接和并发的模块:limit_req_zone用于限制单位时间内的请求数,即ratelimit,使用的是漏桶算法“leakybucket”。limit_req_conn用于限制同时连接数,即并发限制。limit_req_zone参数配置语法:limit_reqzone=name[burst=number][nodelay];默认值:—上下文:http,服务器,locationlimit_req_zone$binary_remote_addrzone=one:10mrate=1r/saddr;第一个参数是通过$_binary_reremote_addrflag来限制的,“binary_”的目的是为了简化内存使用和限制相同的客户端ip地址。第二个参数:zone=one:10m表示生成一块大小为10M,名称为one的内存区域,用于存放访问频率信息。第三个参数:rate=1r/s表示允许相同标识的客户端访问频率。这里限制为每秒1次,也可以是30r/m。limit_reqzone=oneburst=5nodelay;第一个参数:zone=one设置使用哪个配置zone进行限制,对应上面limit_req_zone中的name。第二个参数:burst=5,重点解释一下这个配置,burstburst的意思,这个配置的意思是在大量请求(突发)过来的时候,设置一个缓冲区大小为5,超过访问频率限制的请求吧可以先放入这个缓冲区。第三个参数:nodelay,如果设置了,当访问频率超过,缓冲区已满时,会直接返回503。如果未设置,所有请求将排队等待。示例:http{limit_req_zone$binary_remote_addrzone=one:10mrate=1r/s;server{localch/{limit_reqzone=oneburst=5nodlay;}}下面的配置可以限制特定的UA(比如search.limit_req_zone$anti_spiderzone=one:10mrate=10r/s;limit_reqzone=oneburst=100nodelay;if($http_user_agent~*"googlebot|bingbot|Feedfetcher-Google"){set$anti_spider$http_user_agent;}其他参数,server,location当服务器受限或缓存受限时,将配置写入日志,delayed记录比rejected记录低一级。示例:limit_req_log_level通知延迟基本上是信息。语法:limit_req_status代码;默认值:limit_req_status503;Context:http,server,location设置被拒绝请求的返回值。值只能设置在400到599之间。ngx_http_limit_conn_module参数配置该模块限制单个IP的请求数。并非所有连接都被计算在内。仅当服务器已处理请求并已读取整个请求标头时才计算连接数。语法:limit_conn防区号;默认值:——上下文:http,服务器,位置limit_conn_zone$binary_remote_addrzone=addr:10m;limit_conn_zone$binary_remote_addrzone=perip:10m;limit_conn_zone$server_namezone=perserver:10m;服务器{...limit_connperip10;limit_conn服务器100;可以配置多个limit_conn指令。例如,上面的配置将限制每个客户端IP到服务器的连接数,同时限制到虚拟服务器的连接总数。语法:limit_conn_zonekeyzone=name:size;默认值:—上下文:httplimit_conn_zone$binary_remote_addrzone=addr:10m;此处,客户端IP地址用作密钥。请注意,使用$binary_remote_addr变量而不是$remote_addr。$remote_addr变量的大小可以从7到15个字节不等。存储状态在32位平台上占用32或64字节的内存,在64位平台上始终占用64字节。$binary_remote_addr变量的大小对于IPv4地址始终为4字节,对于IPv6地址始终为16字节。存储状态在32位平台上始终占用32或64字节,在64位平台上始终占用64字节。一个兆字节区域可以容纳大约32000个32字节状态或大约16000个64字节状态。如果区域存储已用完,服务器将向所有其他请求返回错误。语法:limit_conn_log_level公告|警告|错误;默认值:limit_conn_log_level错误;上下文:http、服务器、位置当服务器限制连接数时设置所需的日志记录级别。语法:limit_conn_status代码;默认值:limit_conn_status503;Context:http,server,location设置被拒绝请求的返回值。2、实例1限制访问速率limit_req_zone$binary_remote_addrzone=mylimit:10mrate=2r/s;服务器{位置/{limit_reqzone=mylimit;}}以上规则限制每个IP的访问速度为2r/s,并将此规则应用到根目录。如果一个IP在很短的时间内并发发送多个请求会怎样?单个IP10ms内发送6个请求我们使用单个IP发送,10ms内发送6个请求,只有1个成功,其余5个被拒绝。我们设置的速度是2r/s,为什么只有一个成功?Nginx的限制是不是错了?当然不是,因为Nginx的限流统计是以毫秒为单位的,而我们设置的速度是2r/s。转换后,单个IP在500ms内只允许通过1次请求,从501ms开始允许通过第二次请求。示例2突发缓存处理可以看到我们在短时间内发送了大量的请求,Nginx以毫秒级精度统计,超过限制的请求直接拒绝。这在实际场景中太苛刻了。在真实的网络环境中,请求并不是以统一的速度到达的。请求很可能会“爆”,即“一个接一个”。Nginx考虑到这种情况,可以使用burst关键字来开启对突发请求的缓存处理,而不是直接拒绝。看我们的配置:limit_req_zone$binary_remote_addrzone=mylimit:10mrate=2r/s;服务器{位置/{limit_reqzone=mylimitburst=4;我们添加了burst=4,这意味着每个key(这里是每个IP)最多允许4个burst请求。如果单个IP在10ms内发送6个请求会怎样?与实例1相比,成功的连发次数增加了4次,我们设置的连发次数是一致的。具体处理流程为:1个请求立即处理,4个请求放入突发队列,另一个请求拒绝。通过burst参数,我们让Nginx限流具有缓存和处理突发流量的能力。但是请注意:burst的作用是让多余的请求先入队,慢慢处理。如果不加nodelay参数,队列中的请求不会立即处理,而是按照rate设置的速度,以毫秒级的精准速度慢慢处理。例3Nodelay减少排队时间例2中我们可以看到,通过设置burst参数,可以让Nginx缓存一定程度的burst,冗余的请求可以先放入队列,慢慢处理,起到了一定的作用在疏通交通方面的作用。但是如果队列设置的比较大,请求排队的时间会比较长。从用户的角度来看,RT变长了,对用户很不友好。解决办法是什么?nodelay参数可以让请求在入队时立即处理,也就是说只要请求能进入突发队列,就会立即被后台worker处理。请注意,这意味着当burst设置为nodelay时,系统的瞬时QPS可能会超过rate设置的阈值。nodelay参数只有和burst一起使用时才有效。继续示例2的配置,我们添加nodelay选项:limit_req_zone$binary_remote_addrzone=mylimit:10mrate=2r/s;服务器{位置/{limit_reqzone=mylimitburst=4nodelay;}}单个IP在10ms内并发发送6个请求,结果如下:对比实例2,设置burst和nodela时请求成功率没有变化,但整体耗时变短。这怎么解释呢?例2中,4个请求放入突发队列,工作进程每500ms(rate=2r/s)取一个请求进行处理,最后一个请求会排队2s再处理;例3中,请求入队和例2一样,不同的是队列中的请求是同时有资格被处理的,所以例3中的5个请求可以说是在处理同样的时间,花费的时间自然就缩短了。但是请注意,虽然设置burst和nodelay可以减少burst请求的处理时间,但从长远来看并不会增加吞吐量的上限。长期吞吐量的上限是由速率决定的,因为nodelay只能保证突发请求被立即处理,但是Nginx会限制队列元素释放的速度,就像限制token在队列中产生的速度一样令牌桶。看到这里,你可能会问,加入nodelay参数后的限速算法是个什么“桶”,是漏桶算法还是令牌桶算法?当然,它仍然是一个漏桶算法。考虑一种情况,当令牌桶算法的令牌耗尽时会发生什么?由于它有一个请求队列,它会缓存下一个请求,缓存受队列大小的限制。但是此时缓存这些请求有意义吗?如果服务器过载,缓存队列会越来越长,RT会越来越高。即使请求在很长时间后得到处理,对用户来说也没有什么价值。所以当token不够的时候,最明智的做法就是直接拒绝用户的请求,这就变成了漏桶算法。示例4自定义返回值limit_req_zone$binary_remote_addrzone=mylimit:10mrate=2r/s;服务器{位置/{limit_reqzone=mylimitburst=4nodelay;limit_req_status598;defaultconfigurationstatus}}自定义状态返回值的状态未配置:如果自定义返回值有错误或其他问题,欢迎留言指正。如果有帮助,请点赞+转发分享。欢迎大家关注米公公的公众号:米公公的技术之路
