ngx_http_limit_req 模块的作用是限制request的接收的速率。
配置项分为两种,一种是对于全局的设置。
在http域内设置limit_req_zone内存区域。
limit_req_zone $binary_remote_addr zone=one:64K rate=1r/s;
这里使用binary_remote_addr的意义在于可以有效地节省内存,一个binary_remote_addr是定长的,长度为64B。1m的req_zone 可以容纳1600个不同的客户端的地址。
这样就可以更有效地使用内存。
当用户发送到nginx的请求过快的时候,nginx就会延迟这些请求的处理。保证在rate范围内。
在location字段使用如下配置可以实现对某个location的限流,使用非常灵活。如果不在任何一个location里面写,那么就没有起到限流的作用。
limit_req zone=one burst=1;
值得注意的是限流的zone的大小必须要超过8KB,如果太小,在解析的时候,nginx就会报错。
1.负责分析conf文件中的limit_req_zone相应的字段。
static char *
ngx_http_limit_req_zone(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
根据配置文件生成的req_zone是通过共享内存实现的。
以下是使用limit_req模块的调用栈。
ngx_http_init_phase_handlers 这个函数是为各个阶段挂载handler函数。
ngx_http_core_run_phases这个函数会调用各个阶段的处理函数,到不同的阶段,每个阶段又有自己的不同的处理函数:
每个模块都有自己的模块的相应的信息,用户关于这个模块的配置信息在ngx_http_limit_req_conf_t中,ngx_http_get_module_loc_conf可以获得这个结构体。
-
static ngx_int_t
-
ngx_http_limit_req_handler(ngx_http_request_t *r)
-
{
-
size_t len, n;
-
uint32_t hash;
-
ngx_int_t rc;
-
ngx_uint_t excess;
-
ngx_time_t *tp;
-
ngx_rbtree_node_t *node;
-
ngx_http_variable_value_t *vv;
-
ngx_http_limit_req_ctx_t *ctx;
-
ngx_http_limit_req_node_t *lr;
-
ngx_http_limit_req_conf_t *lrcf;
-
-
if (r->main->limit_req_set) {
-
return NGX_DECLINED;
-
}
-
-
lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module); 获得该模块的location的配置信息
-
-
if (lrcf->shm_zone == NULL) {
-
return NGX_DECLINED;
-
}
-
-
ctx = lrcf->shm_zone->data;设置的共享内存zone
-
-
vv = ngx_http_get_indexed_variable(r, ctx->index);
-
-
if (vv == NULL || vv->not_found) {
-
return NGX_DECLINED;
-
}
-
-
len = vv->len;
-
-
if (len == 0) {
-
return NGX_DECLINED;
-
}
-
-
if (len > 65535) {
-
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-
"the value of the \"%V\" variable "
-
"is more than 65535 bytes: \"%v\"",
-
&ctx->var, vv);
-
return NGX_DECLINED;
-
}
-
-
r->main->limit_req_set = 1;
-
-
hash = ngx_crc32_short(vv->data, len);通过对数据进行hash映射。
-
-
ngx_shmtx_lock(&ctx->shpool->mutex);
-
-
ngx_http_limit_req_expire(ctx, 1);替换掉队列和红黑树当中的不符合条件的请求。
-
-
rc = ngx_http_limit_req_lookup(lrcf, hash, vv->data, len, &excess);
-
-
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-
"limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000);
-
-
if (rc == NGX_DECLINED) {
-
-
n = offsetof(ngx_rbtree_node_t, color)
-
+ offsetof(ngx_http_limit_req_node_t, data)
-
+ len;
-
-
node = ngx_slab_alloc_locked(ctx->shpool, n);
-
if (node == NULL) {
-
-
ngx_http_limit_req_expire(ctx, 0);
-
-
node = ngx_slab_alloc_locked(ctx->shpool, n);
-
if (node == NULL) {
-
ngx_shmtx_unlock(&ctx->shpool->mutex);
-
return NGX_HTTP_SERVICE_UNAVAILABLE;
-
}
-
}
-
-
lr = (ngx_http_limit_req_node_t *) &node->color;
-
-
node->key = hash;
-
lr->len = (u_char) len;
-
-
tp = ngx_timeofday();
-
lr->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
-
-
lr->excess = 0;
-
ngx_memcpy(lr->data, vv->data, len);
-
-
ngx_rbtree_insert(&ctx->sh->rbtree, node);将这个新的请求插入到该模块的红黑树当中。
-
-
ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);将这个请求插入到FIFO队列当中。
-
-
ngx_shmtx_unlock(&ctx->shpool->mutex);
-
-
return NGX_DECLINED;
-
}
-
-
ngx_shmtx_unlock(&ctx->shpool->mutex);
-
-
if (rc == NGX_OK) {
-
return NGX_DECLINED;
-
}
-
-
if (rc == NGX_BUSY) {
-
ngx_log_error(lrcf->limit_log_level, r->connection->log, 0,
-
"limiting requests, excess: %ui.%03ui by zone \"%V\"",
-
excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name);
-
-
return NGX_HTTP_SERVICE_UNAVAILABLE;
-
}
-
-
/* rc == NGX_AGAIN */
-
-
if (lrcf->nodelay) {
-
return NGX_DECLINED;
-
}
-
-
ngx_log_error(lrcf->delay_log_level, r->connection->log, 0,
-
"delaying request, excess: %ui.%03ui, by zone \"%V\"",
-
excess / 1000, excess % 1000, &lrcf->shm_zone->shm.name);
-
-
if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
-
return NGX_HTTP_INTERNAL_SERVER_ERROR;
-
}
-
-
r->read_event_handler = ngx_http_test_reading;
-
r->write_event_handler = ngx_http_limit_req_delay;
-
ngx_add_timer(r->connection->write,
-
(ngx_msec_t) excess * 1000 / ctx->rate);
-
-
return NGX_AGAIN;
-
}
下面的这个函数处理过期的请求,将他们从红黑树和队列中删除。
-
static void
-
ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
-
{
-
ngx_int_t excess;
-
ngx_time_t *tp;
-
ngx_msec_t now;
-
ngx_queue_t *q;
-
ngx_msec_int_t ms;
-
ngx_rbtree_node_t *node;
-
ngx_http_limit_req_node_t *lr;
-
-
tp = ngx_timeofday();
-
-
now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
-
-
/*
-
* n == 1 deletes one or two zero rate entries
-
* n == 0 deletes oldest entry by force
-
* and one or two zero rate entries
-
*/
-
-
while (n < 3) {
-
-
if (ngx_queue_empty(&ctx->sh->queue)) {
-
return;
-
}
-
1)获得队列中最早的一个请求
-
q = ngx_queue_last(&ctx->sh->queue);
-
-
lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
-
-
if (n++ != 0) {
-
-
ms = (ngx_msec_int_t) (now - lr->last);
-
ms = ngx_abs(ms);
-
-
if (ms < 60000) {
-
return;
-
}
-
计算这个请求的到来的时间,看这个请求是否
-
excess = lr->excess - ctx->rate * ms / 1000;
-
-
if (excess > 0) {
-
return;
-
}
-
}
-
-
ngx_queue_remove(q);
-
-
node = (ngx_rbtree_node_t *)
-
((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
-
-
ngx_rbtree_delete(&ctx->sh->rbtree, node);
-
-
ngx_slab_free_locked(ctx->shpool, node);
-
}
-
}
-
static void
-
ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
-
{
-
ngx_int_t excess;
-
ngx_time_t *tp;
-
ngx_msec_t now;
-
ngx_queue_t *q;
-
ngx_msec_int_t ms;
-
ngx_rbtree_node_t *node;
-
ngx_http_limit_req_node_t *lr;
-
-
tp = ngx_timeofday();
-
-
now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
-
-
/*
-
* n == 1 deletes one or two zero rate entries
-
* n == 0 deletes oldest entry by force
-
* and one or two zero rate entries
-
*/
-
-
while (n < 3) {
-
-
if (ngx_queue_empty(&ctx->sh->queue)) {
-
return;
-
}
-
-
q = ngx_queue_last(&ctx->sh->queue);
-
-
lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
-
-
if (n++ != 0) {
-
-
ms = (ngx_msec_int_t) (now - lr->last);
-
ms = ngx_abs(ms);
-
-
if (ms < 60000) {
-
return;
-
}
-
-
excess = lr->excess - ctx->rate * ms / 1000;
-
-
if (excess > 0) {
-
return;
-
}
-
}
-
-
ngx_queue_remove(q);
-
-
node = (ngx_rbtree_node_t *)
-
((u_char *) lr - offsetof(ngx_rbtree_node_t, color));
-
-
ngx_rbtree_delete(&ctx->sh->rbtree, node);
-
-
ngx_slab_free_locked(ctx->shpool, node);
-
}
-
}
阅读(8162) | 评论(0) | 转发(0) |