算法思想是:html
算法思想是:node
从做用上来讲,漏桶和令牌桶算法最明显的区别就是是否容许突发流量(burst)的处理,漏桶算法可以强行限制数据的实时传输(处理)速率,对突发流量不作额外处理;而令牌桶算法可以在限制数据的平均传输速率的同时容许某种程度的突发传输。nginx
Nginx按请求速率限速模块使用的是漏桶算法,即可以强行保证请求的实时处理速度不会超过设置的阈值。算法
Nginx官方版本限制IP的链接和并发分别有两个模块:shell
limit_req_zone
用来限制单位时间内的请求数,即速率限制,采用的漏桶算法 "leaky bucket"。limit_req_conn
用来限制同一时间链接数,即并发限制。Syntax: limit_req zone=name [burst=number] [nodelay]; Default: — Context: http, server, location
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
缓存
limit_req zone=one burst=5 nodelay;
服务器
例子:网络
http { limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s; server { location /search/ { limit_req zone=one burst=5 nodelay; } }
下面配置能够限制特定UA(好比搜索引擎)的访问:并发
limit_req_zone $anti_spider zone=one:10m rate=10r/s; limit_req zone=one burst=100 nodelay; if ($http_user_agent ~* "googlebot|bingbot|Feedfetcher-Google") { set $anti_spider $http_user_agent; }
其余参数ide
Syntax: limit_req_log_level info | notice | warn | error; Default: limit_req_log_level error; Context: http, server, location
当服务器因为limit被限速或缓存时,配置写入日志。延迟的记录比拒绝的记录低一个级别。例子:limit_req_log_level notice
延迟的的基本是info。
Syntax: limit_req_status code; Default: limit_req_status 503; Context: http, server, location
设置拒绝请求的返回值。值只能设置 400 到 599 之间。
这个模块用来限制单个IP的请求数。并不是全部的链接都被计数。只有在服务器处理了请求而且已经读取了整个请求头时,链接才被计数。
Syntax: limit_conn zone number; Default: — Context: http, server, location
limit_conn_zone $binary_remote_addr zone=addr:10m; server { location /download/ { limit_conn addr 1; }
一次只容许每一个IP地址一个链接。
limit_conn_zone $binary_remote_addr zone=perip:10m; limit_conn_zone $server_name zone=perserver:10m; server { ... limit_conn perip 10; limit_conn perserver 100; }
能够配置多个limit_conn指令。例如,以上配置将限制每一个客户端IP链接到服务器的数量,同时限制链接到虚拟服务器的总数。
Syntax: limit_conn_zone key zone=name:size; Default: — Context: http
limit_conn_zone $binary_remote_addr zone=addr:10m;
在这里,客户端IP地址做为关键。请注意,不是$ remote_addr
,而是使用$ binary_remote_addr
变量。 $ remote_addr
变量的大小能够从7到15个字节不等。存储的状态在32位平台上占用32或64字节的内存,在64位平台上老是占用64字节。对于IPv4地址,$ binary_remote_addr
变量的大小始终为4个字节,对于IPv6地址则为16个字节。存储状态在32位平台上始终占用32或64个字节,在64位平台上占用64个字节。一个兆字节的区域能够保持大约32000个32字节的状态或大约16000个64字节的状态。若是区域存储耗尽,服务器会将错误返回给全部其余请求。
Syntax: limit_conn_log_level info | notice | warn | error; Default: limit_conn_log_level error; Context: http, server, location
当服务器限制链接数时,设置所需的日志记录级别。
Syntax: limit_conn_status code; Default: limit_conn_status 503; Context: http, server, location
设置拒绝请求的返回值。
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s; server { location / { limit_req zone=mylimit; } }
上述规则限制了每一个IP访问的速度为2r/s,并将该规则做用于根目录。若是单个IP在很是短的时间内并发发送多个请求,结果会怎样呢?
咱们使用单个IP在10ms内发并发送了6个请求,只有1个成功,剩下的5个都被拒绝。咱们设置的速度是2r/s,为何只有1个成功呢,是否是Nginx限制错了?固然不是,是由于Nginx的限流统计是基于毫秒的,咱们设置的速度是2r/s,转换一下就是500ms内单个IP只容许经过1个请求,从501ms开始才容许经过第二个请求。
咱们看到,咱们短期内发送了大量请求,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个请求,结果会怎样呢?
相比实例一成功数增长了4个,这个咱们设置的burst数目是一致的。具体处理流程是:1个请求被当即处理,4个请求被放到burst队列里,另一个请求被拒绝。经过burst参数,咱们使得Nginx限流具有了缓存处理突发流量的能力。
可是请注意:burst的做用是让多余的请求能够先放到队列里,慢慢处理。若是不加nodelay参数,队列里的请求不会当即处理,而是按照rate设置的速度,以毫秒级精确的速度慢慢处理。
实例二中咱们看到,经过设置burst参数,咱们能够容许Nginx缓存处理必定程度的突发,多余的请求能够先放到队列里,慢慢处理,这起到了平滑流量的做用。可是若是队列设置的比较大,请求排队的时间就会比较长,用户角度看来就是RT变长了,这对用户很不友好。有什么解决办法呢?nodelay参数容许请求在排队的时候就当即被处理,也就是说只要请求可以进入burst队列,就会当即被后台worker处理,请注意,这意味着burst设置了nodelay时,系统瞬间的QPS可能会超过rate设置的阈值。nodelay参数要跟burst一块儿使用才有做用。
延续实例二的配置,咱们加入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个请求,结果以下:
跟实例二相比,请求成功率没变化,可是整体耗时变短了。这怎么解释呢?实例二中,有4个请求被放到burst队列当中,工做进程每隔500ms(rate=2r/s)取一个请求进行处理,最后一个请求要排队2s才会被处理;实例三中,请求放入队列跟实例二是同样的,但不一样的是,队列中的请求同时具备了被处理的资格,因此实例三中的5个请求能够说是同时开始被处理的,花费时间天然变短了。
可是请注意,虽然设置burst和nodelay可以下降突发请求的处理时间,可是长期来看并不会提升吞吐量的上限,长期吞吐量的上限是由rate决定的,由于nodelay只能保证burst的请求被当即处理,但Nginx会限制队列元素释放的速度,就像是限制了令牌桶中令牌产生的速度。
看到这里你可能会问,加入了nodelay参数以后的限速算法,到底算是哪个“桶”,是漏桶算法仍是令牌桶算法?固然还算是漏桶算法。考虑一种状况,令牌桶算法的token为耗尽时会怎么作呢?因为它有一个请求队列,因此会把接下来的请求缓存下来,缓存多少受限于队列大小。但此时缓存这些请求还有意义吗?若是server已通过载,缓存队列愈来愈长,RT愈来愈高,即便过了好久请求被处理了,对用户来讲也没什么价值了。因此当token不够用时,最明智的作法就是直接拒绝用户的请求,这就成了漏桶算法。
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s; server { location / { limit_req zone=mylimit burst=4 nodelay; limit_req_status 598; } }
默认状况下 没有配置 status 返回值的状态:
自定义 status 返回值的状态:
Nginx限制访问速率和最大并发链接数模块--limit (防止DDOS攻击)
Nginx 限流
关于nginx的限速模块
Nginx 源代码笔记 - HTTP 模块 - 流控
Module ngx_http_limit_conn_module
Module ngx_http_limit_req_module
Nginx限速模块初探