##nginx.conf配置文件limit_req配置
##nginx限制访问频率,限流
http {
limit_req_zone $binary_remote_addr zone=one:30m rate=1r/s;
server {
listen 8103;
server_name localhost;
location ~ ^/yym/ {
root /home/yym/nginx_web/yym-vue3/;
index index.html index.htm;
try_files $uri $uri/ /index.html;
limit_req zone=one burst=1 nodelay;
#limit_conn addr 1;
#limit_conn_status 503;
#limit_rate_after 10240K;
#limit_rate 50k;
}
}
}
##ngx_http_limit_req_init方法
#0 ngx_http_limit_req_init (cf=0x7fffffffdc80) at src/http/modules/ngx_http_limit_req_module.c:1092
#1 0x00005555555b8a89 in ngx_http_block (cf=0x7fffffffdc80, cmd=0x5555556640a0 <ngx_http_commands>, conf=0x555555696988) at src/http/ngx_http.c:310
#2 0x0000555555594031 in ngx_conf_handler (cf=0x7fffffffdc80, last=1) at src/core/ngx_conf_file.c:463
#3 0x0000555555593b34 in ngx_conf_parse (cf=0x7fffffffdc80, filename=0x5555556958e8) at src/core/ngx_conf_file.c:319
#4 0x000055555558f2fb in ngx_init_cycle (old_cycle=0x7fffffffde50) at src/core/ngx_cycle.c:284
#5 0x000055555556e66b in main (argc=3, argv=0x7fffffffe1f8) at src/core/nginx.c:292
##ngx_http_limit_req_handler函数指针初始化到cmcf结构体
ngx_http_core_main_conf_t *cmcf;
*h = ngx_http_limit_req_handler;
指针函数赋予cmcf结构体handlers
##ngx_http_limit_req_handler方法
#0 ngx_http_limit_req_handler (r=0x5555556c6fd0) at src/http/modules/ngx_http_limit_req_module.c:196
#1 0x00005555555bcd66 in ngx_http_core_generic_phase (r=0x5555556c6fd0, ph=0x5555556c92c0) at src/http/ngx_http_core_module.c:897
#2 0x00005555555bcd00 in ngx_http_core_run_phases (r=0x5555556c6fd0) at src/http/ngx_http_core_module.c:875
#3 0x00005555555bcc69 in ngx_http_handler (r=0x5555556c6fd0) at src/http/ngx_http_core_module.c:858
#4 0x00005555555cb829 in ngx_http_process_request (r=0x5555556c6fd0) at src/http/ngx_http_request.c:2120
#5 0x00005555555ca302 in ngx_http_process_request_headers (rev=0x5555556cd5b0) at src/http/ngx_http_request.c:1498
#6 0x00005555555c981e in ngx_http_process_request_line (rev=0x5555556cd5b0) at src/http/ngx_http_request.c:1165
#7 0x00005555555c8f10 in ngx_http_wait_request_handler (rev=0x5555556cd5b0) at src/http/ngx_http_request.c:503
#8 0x00005555555b6e69 in ngx_epoll_process_events (cycle=0x5555556956d0, timer=60000, flags=1) at src/event/modules/ngx_epoll_module.c:901
#9 0x00005555555a63ca in ngx_process_events_and_timers (cycle=0x5555556956d0) at src/event/ngx_event.c:248
#10 0x00005555555b4701 in ngx_worker_process_cycle (cycle=0x5555556956d0, data=0x0) at src/os/unix/ngx_process_cycle.c:721
#11 0x00005555555b1309 in ngx_spawn_process (cycle=0x5555556956d0, proc=0x5555555b4645 <ngx_worker_process_cycle>, data=0x0, name=0x555555646757 "worker process", respawn=-3)
at src/os/unix/ngx_process.c:199
#12 0x00005555555b3898 in ngx_start_worker_processes (cycle=0x5555556956d0, n=1, type=-3) at src/os/unix/ngx_process_cycle.c:344
#13 0x00005555555b3069 in ngx_master_process_cycle (cycle=0x5555556956d0) at src/os/unix/ngx_process_cycle.c:130
#14 0x000055555556ea45 in main (argc=3, argv=0x7fffffffe1f8) at src/core/nginx.c:383
##ngx_http_complex_value方法 if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) {
key 使用请求来源ip
1、计算key:通过ngx_http_complex_value函数,基于请求和配置中的key指令计算出实际的key值
2、检查key值:计算出的key值会被检查是否为空或过长
ngx_int_t
ngx_http_complex_value(ngx_http_request_t *r, ngx_http_complex_value_t *val,
ngx_str_t *value)
{
size_t len;
ngx_http_script_code_pt code;
ngx_http_script_len_code_pt lcode;
ngx_http_script_engine_t e;
if (val->lengths == NULL) {
*value = val->value;
return NGX_OK;
}
ngx_http_script_flush_complex_value(r, val);
ngx_memzero(&e, sizeof(ngx_http_script_engine_t));
e.ip = val->lengths;
e.request = r;
e.flushed = 1;
len = 0;
while (*(uintptr_t *) e.ip) {
lcode = *(ngx_http_script_len_code_pt *) e.ip;
len += lcode(&e);
}
value->len = len;
value->data = ngx_pnalloc(r->pool, len);
if (value->data == NULL) {
return NGX_ERROR;
}
e.ip = val->values;
e.pos = value->data;
e.buf = *value;
while (*(uintptr_t *) e.ip) {
code = *(ngx_http_script_code_pt *) e.ip;
code((ngx_http_script_engine_t *) &e);
}
*value = e.buf;
return NGX_OK;
}
##ngx_http_limit_req_lookup 函数中,处理未找到匹配键并准备在红黑树中插入新节点的部分
static ngx_int_t
ngx_http_limit_req_lookup(ngx_http_limit_req_limit_t *limit, ngx_uint_t hash,
ngx_str_t *key, ngx_uint_t *ep, ngx_uint_t account)
{
size_t size;
ngx_int_t rc, excess;
ngx_msec_t now;
ngx_msec_int_t ms;
ngx_rbtree_node_t *node, *sentinel;
ngx_http_limit_req_ctx_t *ctx;
ngx_http_limit_req_node_t *lr;
now = ngx_current_msec;
ctx = limit->shm_zone->data;
node = ctx->sh->rbtree.root;
sentinel = ctx->sh->rbtree.sentinel;
while (node != sentinel) {
if (hash < node->key) {
node = node->left;
continue;
}
if (hash > node->key) {
node = node->right;
continue;
}
/* hash == node->key */
lr = (ngx_http_limit_req_node_t *) &node->color;
rc = ngx_memn2cmp(key->data, lr->data, key->len, (size_t) lr->len);
if (rc == 0) {
ngx_queue_remove(&lr->queue);
ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
ms = (ngx_msec_int_t) (now - lr->last);
if (ms < -60000) {
ms = 1;
} else if (ms < 0) {
ms = 0;
}
excess = lr->excess - ctx->rate * ms / 1000 + 1000;
if (excess < 0) {
excess = 0;
}
*ep = excess;
if ((ngx_uint_t) excess > limit->burst) {
return NGX_BUSY;
}
if (account) {
lr->excess = excess;
if (ms) {
lr->last = now;
}
return NGX_OK;
}
lr->count++;
ctx->node = lr;
return NGX_AGAIN;
}
node = (rc < 0) ? node->left : node->right;
}
*ep = 0;
size = offsetof(ngx_rbtree_node_t, color)
+ offsetof(ngx_http_limit_req_node_t, data)
+ key->len;
ngx_http_limit_req_expire(ctx, 1);
node = ngx_slab_alloc_locked(ctx->shpool, size);
if (node == NULL) {
ngx_http_limit_req_expire(ctx, 0);
node = ngx_slab_alloc_locked(ctx->shpool, size);
if (node == NULL) {
ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
"could not allocate node%s", ctx->shpool->log_ctx);
return NGX_ERROR;
}
}
node->key = hash;
lr = (ngx_http_limit_req_node_t *) &node->color;
lr->len = (u_short) key->len;
lr->excess = 0;
ngx_memcpy(lr->data, key->data, key->len);
ngx_rbtree_insert(&ctx->sh->rbtree, node);
ngx_queue_insert_head(&ctx->sh->queue, &lr->queue);
if (account) {
lr->last = now;
lr->count = 0;
return NGX_OK;
}
lr->last = 0;
lr->count = 1;
ctx->node = lr;
return NGX_AGAIN;
}
##ngx_http_limit_req_expire尝试清理过期节点,ngx_slab_alloc_locked分配node空间
ngx_http_limit_req_expire(ctx, 1);
node = ngx_slab_alloc_locked(ctx->shpool, size);
##共享内存初始化,分配内存
if (ngx_shm_alloc(&shm_zone[i].shm) != NGX_OK) {
goto failed;
}
ngx_int_t
ngx_shm_alloc(ngx_shm_t *shm)
{
shm->addr = (u_char *) mmap(NULL, shm->size,
PROT_READ|PROT_WRITE,
MAP_ANON|MAP_SHARED, -1, 0);
if (shm->addr == MAP_FAILED) {
ngx_log_error(NGX_LOG_ALERT, shm->log, ngx_errno,
"mmap(MAP_ANON|MAP_SHARED, %uz) failed", shm->size);
return NGX_ERROR;
}
return NGX_OK;
}
#0 ngx_shm_alloc (shm=0x555555695d70) at src/os/unix/ngx_shmem.c:17
#1 0x000055555558fdbb in ngx_init_cycle (old_cycle=0x7fffffffde60) at src/core/ngx_cycle.c:485
#2 0x000055555556e66b in main (argc=3, argv=0x7fffffffe208) at src/core/nginx.c:292
(gdb) p *shm
$1 = {addr = 0x0, size = 31457280, name = {len = 3, data = 0x5555556aa179 "one:30m"}, log = 0x5555556956e8, exists = 0}