Nginx模块开发(10)—limit_req模块分析

1、命令简介

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

语法:limit_req_zone  $session_variable  zone=name:size  rate=rate

默认值:none

上下文:http

命令解析:session会话状态分配一个大小为size的内存存储区,限制了每秒(分、小时)只接受rateIP的频率。

limit_req

语法:limit_req  zone=name  burst=burst  [nodelay]

默认值:none

上下文:httpserverlocation

命令解析:该指令用于指定使用的内存存储区(zone)名称,以及最大的突发请求数(burse)。如果请求的速率超过了limit_req_zone指令中设置的速率,这些请求将被延迟处理,在这种情况下,请求获得服务不可用信息,返回503状态码。

2、相关结构体

ngx_http_limit_req_conf_t

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指令的相关信息。

ngx_http_limit_req_node_t

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;

备注:该结构用于存放每个客户端节点的相关信息。

ngx_http_limit_req_shctx_t

typedef struct {

    ngx_rbtree_t                  rbtree;

    ngx_rbtree_node_t             sentinel;

    ngx_queue_t                  queue;

} ngx_http_limit_req_shctx_t;

备注:该结构体用于管理客户端节点信息,方式主要有队列和红黑树。

ngx_http_limit_req_ctx_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指令的相关信息。

结构体关系

Nginx模块开发(10)—limit_req模块分析 - cjhust - 我一直在努力

3、功能函数

3.1 初始化ctx

ngx_http_limit_req_create_conf(ngx_conf_t *cf);

函数功能:创建limit_req模块使用的结构体ngx_http_limit_req_conf_t,并初始化。

ngx_http_limit_req_merge_conf(ngx_conf_t *cf, void *parent,

void *child);

函数功能:location级别合并上级的配置,即server级别配置的limit_reqlocation级别也是有效的。

ngx_http_limit_req_init(ngx_conf_t *cf)

函数功能:ngx_http_limit_req_handler添加到&cmcf->phases[NGX_HTTP_PREACCESS_PHASE].handlers的处理数组中,即在nginx处理用户请求前调用ngx_http_limit_req_handler函数。

3.2 初始化commands

ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

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_addrindex

    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函数初始化shshpool

ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data)

函数功能:初始化共享内存,nginx使用共享内存的主要目的是进程间通信,并且可以在用户使用kill –HUP的时候,数据不被清零,即起到了数据累积的作用;

ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)

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中。

3.2 数据handle

ngx_http_limit_req_handler(ngx_http_request_t *r)

函数功能:请求的处理函数。主要步骤如下所示:

1lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module),读取位置部分的配置到lrcf中;

2vv = ngx_http_get_indexed_variable(r, ctx->index),根据索引得到变量值如$binary_remote_addr

3ngx_http_limit_req_expire(ctx, 1),删除too old的节点;

4rc=ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &excess),查找客户端节点信息,并得到相应的状态信息如NGX_BUSYNGX_AGAINNGX_OKNGX_ DECLINED

5)根据rc的返回值,进行相应的处理;

ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)

函数功能:删除too old的节点。

ngx_http_limit_req_delay(ngx_http_request_t *r);

函数功能:延迟处理用户请求。

ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lrcf…)

函数功能:通过红黑树查找用户节点,并对节点的两次访问间隔频率进行计算,返回NGX_BUSYNGX_AGAINNGX_OKNGX_ DECLINED4种状态。

1NGX_ DECLINED:节点不存在;

2NGX_OK:该客户端访问频率未超过设定值;

3NGX_AGAIN:该客户端访问频率超过了设定值,但是并未超过阈值(与burst有关);

4NGX_BUSY:该客户端访问频率超过了阈值;

 

备注:该函数是整个模块的核心,算法思想是令牌桶算法。令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常用的一种算法。典型情况下,令牌桶算法用来控制发送到网络上的数据数目,并允许突发数据的发送。

令牌桶这种控制机制基于令牌桶中是否存在令牌来指示什么时候可以发送流量。令牌桶中的每一个令牌都代表一个字节。如果令牌桶中存在令牌,则允许发送流量;如果令牌桶中不存在令牌,则不允许发送流量。因此,如果突发门限被合理地配置并且令牌桶中有足够的令牌,那么流量就可以以峰值速率发送。令牌桶算法的基本过程如下:

  (1)假如用户配置的平均发送速率为10r/s,则每隔0.1秒一个令牌被加入到桶中;

  (2)假设桶最多可以存发b个令牌。如果令牌到达时令牌桶已经满了,那么这个令牌会被丢弃;

  (3)当一个n个字节的数据包到达时,就从令牌桶中删除n个令牌,并且数据包被发送到网络;

  (4)如果令牌桶中少于n个令牌,那么不会删除令牌,并且认为这个数据包在流量限制之外;

  (5)算法允许最长b个字节的突发,但从长期运行结果看,数据包的速率被限制成常量r。对于在流量限制外的数据包可以以不同的方式处理:

  它们可以被丢弃;

  它们可以排放在队列中以便当令牌桶中累积了足够多的令牌时再传输;

  它们可以继续发送,但需要做特殊标记,网络过载的时候将这些特殊标记的包丢弃。

注意:令牌桶算法不能与另外一种常见算法“漏桶算法(Leaky Bucket)”相混淆。这两种算法的主要区别在于“漏桶算法”能够强行限制数据的传输速率,而“令牌桶算法”在能够限制数据的平均传输数据外,还允许某种程度的突发传输。在“令牌桶算法”中,只要令牌桶中存在令牌,那么就允许突发地传输数据直到达到用户配置的门限,因此它适合于具有突发特性的流量。

 

Nginx模块开发(10)—limit_req模块分析 - cjhust - 我一直在努力 

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;

ngx_http_limit_req_rbtree_insert_value (ngx_rbtree_node_t *temp, ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel)

函数功能:通过红黑树结构插入节点。

5、流程图

模块入口(ngx_module_t

Nginx模块开发(10)—limit_req模块分析 - cjhust - 我一直在努力
ngx_module_t  ngx_http_limit_req_module 是整个模块的入口,它定义了它支持的指令入口和模块配置文件初始化的入口。

模块上下文(ngx_http_module_t

Nginx模块开发(10)—limit_req模块分析 - cjhust - 我一直在努力

 

Nginx模块开发(10)—limit_req模块分析 - cjhust - 我一直在努力
   
这个结构体的主要功能是用来创建三个部分( main server location )的配置和合并配置。主要包括以下几个函数:

1)读入配置文件前调用;

2)读入配置文件后调用;

3)创建全局部分配置时调用;

4)初始化全局部分配置时调用;

5)创建主机部分配置时调用;

6)与全局部分配置合并时调用;

7)创建位置部分配置时调用;

8)与主机部分配置合并时调用;

而在limit_req模块我们关注的是(2)、(7)和(8)。模块的处理顺序是(7->8->2),create函数用来为特定的位置部分的配置结构体分配内存,merge函数用来设定默认值和与继承过来的配置合并,这个合并函数还要负责检验读入的数值是否有效,postconfig函数用来初始化处理函数。

在本模块中,createmerge函数的主要功能是:对模块的日志级别进行设置。

模块指令(ngx_command_t

Nginx模块开发(10)—limit_req模块分析 - cjhust - 我一直在努力
 
Nginx模块开发(10)—limit_req模块分析 - cjhust - 我一直在努力

  

ngx_http_limit_req_zone的主要功能:

1)解析配置文件的命令参数:namesizeK/M)、scaler/sr/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;

你可能感兴趣的:(Nginx模块开发(10)—limit_req模块分析)