http{
limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
server {
location /{
limit_req zone=one burst=5 nodelay;
}
}
}
语法:limit_req_zone $session_variable zone=name:size rate=rate
默认值:none
上下文:http
命令解析:为session会话状态分配一个大小为size的内存存储区,限制了每秒(分、小时)只接受rate个IP的频率。
语法:limit_req zone=name burst=burst [nodelay]
默认值:none
上下文:http、server、location
命令解析:该指令用于指定使用的内存存储区(zone)名称,以及最大的突发请求数(burse)。如果请求的速率超过了limit_req_zone指令中设置的速率,这些请求将被延迟处理,在这种情况下,请求获得服务不可用信息,返回503状态码。
typedef struct {
ngx_shm_zone_t *shm_zone;
ngx_uint_t burst; //1等价于0.001r/s
ngx_uint_t limit_log_level;
ngx_uint_t delay_log_level;
ngx_uint_t nodelay;
} ngx_http_limit_req_conf_t;
备注:该结构用于存放limit_req指令的相关信息。
typedef struct {
u_char color;
u_char dummy;
u_short len;
ngx_queue_t queue;
ngx_msec_t last;
ngx_uint_t excess; //1等价于0.001 r/s
u_char data[1];
} ngx_http_limit_req_node_t;
备注:该结构用于存放每个客户端节点的相关信息。
typedef struct {
ngx_rbtree_t rbtree;
ngx_rbtree_node_t sentinel;
ngx_queue_t queue;
} ngx_http_limit_req_shctx_t;
备注:该结构体用于管理客户端节点信息,方式主要有队列和红黑树。
typedef struct {
ngx_http_limit_req_shctx_t *sh;
ngx_slab_pool_t *shpool;
ngx_uint_t rate; //1等价于0.001 r/s
ngx_int_t index;
ngx_str_t var;
} ngx_http_limit_req_ctx_t;
备注:该结构用于存放limit_req_zone指令的相关信息。
函数功能:创建limit_req模块使用的结构体ngx_http_limit_req_conf_t,并初始化。
void *child);
函数功能:在location级别合并上级的配置,即server级别配置的limit_req在location级别也是有效的。
函数功能:将ngx_http_limit_req_handler添加到&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers的处理数组中,即在nginx处理用户请求前调用ngx_http_limit_req_handler函数。
typedef struct {
ngx_http_limit_req_shctx_t *sh;
ngx_slab_pool_t *shpool;
ngx_uint_t rate; //rate*1000/scale
ngx_int_t index; //存放$binary_remote_addr的index
ngx_str_t var; //存放具体的$binary_remote_addr
} ngx_http_limit_req_ctx_t;
shm_zone->init = ngx_http_limit_req_init_zone;
shm_zone->data = ctx;
函数功能:申请共享内存,并将limit_req_zone的配置参数放入到ngx_http_limit_req_ctx_t结构中,并使用ngx_http_limit_req_init_zone函数初始化sh和shpool。
函数功能:初始化共享内存,nginx使用共享内存的主要目的是进程间通信,并且可以在用户使用kill –HUP的时候,数据不被清零,即起到了数据累积的作用;
typedef struct {
ngx_shm_zone_t *shm_zone;
ngx_uint_t burst;
ngx_uint_t limit_log_level;
ngx_uint_t delay_log_level;
ngx_uint_t nodelay; /* unsigned nodelay:1 */
} ngx_http_limit_req_conf_t;
函数功能:对配置文件的limit_req指令的参数进行解析,并将配置参数加入到结构体ngx_http_limit_req_conf_t中。
函数功能:请求的处理函数。主要步骤如下所示:
(1)lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module),读取位置部分的配置到lrcf中;
(2)vv = ngx_http_get_indexed_variable(r, ctx->index),根据索引得到变量值如$binary_remote_addr;
(3)ngx_http_limit_req_expire(ctx, 1),删除too old的节点;
(4)rc=ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &excess),查找客户端节点信息,并得到相应的状态信息如NGX_BUSY、NGX_AGAIN、NGX_OK、NGX_ DECLINED;
(5)根据rc的返回值,进行相应的处理;
函数功能:删除too old的节点。
函数功能:延迟处理用户请求。
函数功能:通过红黑树查找用户节点,并对节点的两次访问间隔频率进行计算,返回NGX_BUSY、NGX_AGAIN、NGX_OK、NGX_ DECLINED共4种状态。
(1)NGX_ DECLINED:节点不存在;
(2)NGX_OK:该客户端访问频率未超过设定值;
(3)NGX_AGAIN:该客户端访问频率超过了设定值,但是并未超过阈值(与burst有关);
(4)NGX_BUSY:该客户端访问频率超过了阈值;
备注:该函数是整个模块的核心,算法思想是令牌桶算法。令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据数目,并允许突发数据的发送。
令牌桶这种控制机制基于令牌桶中是否存在令牌来指示什么时候可以发送流量。令牌桶中的每一个令牌都代表一个字节。如果令牌桶中存在令牌,则允许发送流量;如果令牌桶中不存在令牌,则不允许发送流量。因此,如果突发门限被合理地配置并且令牌桶中有足够的令牌,那么流量就可以以峰值速率发送。令牌桶算法的基本过程如下:
(1)假如用户配置的平均发送速率为10r/s,则每隔0.1秒一个令牌被加入到桶中;
(2)假设桶最多可以存发b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃;
(3)当一个n个字节的数据包到达时,就从令牌桶中删除n个令牌,并且数据包被发送到网络;
(4)如果令牌桶中少于n个令牌,那么不会删除令牌,并且认为这个数据包在流量限制之外;
(5)算法允许最长b个字节的突发,但从长期运行结果看,数据包的速率被限制成常量r。对于在流量限制外的数据包可以以不同的方式处理:
它们可以被丢弃;
它们可以排放在队列中以便当令牌桶中累积了足够多的令牌时再传输;
它们可以继续发送,但需要做特殊标记,网络过载的时候将这些特殊标记的包丢弃。
注意:令牌桶算法不能与另外一种常见算法“漏桶算法(Leaky Bucket)”相混淆。这两种算法的主要区别在于“漏桶算法”能够强行限制数据的传输速率,而“令牌桶算法”在能够限制数据的平均传输数据外,还允许某种程度的突发传输。在“令牌桶算法”中,只要令牌桶中存在令牌,那么就允许突发地传输数据直到达到用户配置的门限,因此它适合于具有突发特性的流量。
tp = ngx_timeofday();
now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
ms = (ngx_msec_int_t) (now - lr->last);
excess = lr->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
//令牌桶算法:每来一个请求分配一个令牌1000
//rate*ms = 1s内该请求所花的时间< xmlnamespace prefix ="o" ns ="urn:schemas-microsoft-com:office:office" />
//excess = 1s的余量
if (excess < 0) { //频率很低
excess = 0;
}
*ep = excess;
if ((ngx_uint_t) excess > lrcf->burst) { // 1s可以并发burst个请求
return NGX_BUSY;
}
lr->excess = excess;
lr->last = now;
if (excess) {
return NGX_AGAIN;
}
return NGX_OK;
函数功能:通过红黑树结构插入节点。
(1)读入配置文件前调用;
(2)读入配置文件后调用;
(3)创建全局部分配置时调用;
(4)初始化全局部分配置时调用;
(5)创建主机部分配置时调用;
(6)与全局部分配置合并时调用;
(7)创建位置部分配置时调用;
(8)与主机部分配置合并时调用;
而在limit_req模块我们关注的是(2)、(7)和(8)。模块的处理顺序是(7)->(8)->(2),create函数用来为特定的位置部分的配置结构体分配内存,merge函数用来设定默认值和与继承过来的配置合并,这个合并函数还要负责检验读入的数值是否有效,postconfig函数用来初始化处理函数。
在本模块中,create、merge函数的主要功能是:对模块的日志级别进行设置。
ngx_http_limit_req_zone的主要功能:
(1)解析配置文件的命令参数:name、size(K/M)、scale(r/s、r/m)、rate;
(2)申请配置文件空间ngx_http_limit_req_ctx_t,填充参数;
(3)申请共享内存,并初始化ngx_http_limit_req_init_zone;
shm_zone->init = ngx_http_limit_req_init_zone;
shm_zone->data = ctx;