Mercurial > hg > nginx
comparison src/http/modules/ngx_http_limit_conn_module.c @ 7596:b45f052483b8
Limit conn: added shared context.
Previously only an rbtree was associated with a limit_conn. To make it
possible to associate more data with a limit_conn, shared context is introduced
similar to limit_req. Also, shared pool pointer is kept in a way similar to
limit_req.
author | Roman Arutyunyan <arut@nginx.com> |
---|---|
date | Mon, 18 Nov 2019 19:50:59 +0300 |
parents | 9606d93aa586 |
children |
comparison
equal
deleted
inserted
replaced
7595:9606d93aa586 | 7596:b45f052483b8 |
---|---|
14 #define NGX_HTTP_LIMIT_CONN_REJECTED 2 | 14 #define NGX_HTTP_LIMIT_CONN_REJECTED 2 |
15 #define NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN 3 | 15 #define NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN 3 |
16 | 16 |
17 | 17 |
18 typedef struct { | 18 typedef struct { |
19 u_char color; | 19 u_char color; |
20 u_char len; | 20 u_char len; |
21 u_short conn; | 21 u_short conn; |
22 u_char data[1]; | 22 u_char data[1]; |
23 } ngx_http_limit_conn_node_t; | 23 } ngx_http_limit_conn_node_t; |
24 | 24 |
25 | 25 |
26 typedef struct { | 26 typedef struct { |
27 ngx_shm_zone_t *shm_zone; | 27 ngx_shm_zone_t *shm_zone; |
28 ngx_rbtree_node_t *node; | 28 ngx_rbtree_node_t *node; |
29 } ngx_http_limit_conn_cleanup_t; | 29 } ngx_http_limit_conn_cleanup_t; |
30 | 30 |
31 | 31 |
32 typedef struct { | 32 typedef struct { |
33 ngx_rbtree_t *rbtree; | 33 ngx_rbtree_t rbtree; |
34 ngx_http_complex_value_t key; | 34 ngx_rbtree_node_t sentinel; |
35 } ngx_http_limit_conn_shctx_t; | |
36 | |
37 | |
38 typedef struct { | |
39 ngx_http_limit_conn_shctx_t *sh; | |
40 ngx_slab_pool_t *shpool; | |
41 ngx_http_complex_value_t key; | |
35 } ngx_http_limit_conn_ctx_t; | 42 } ngx_http_limit_conn_ctx_t; |
36 | 43 |
37 | 44 |
38 typedef struct { | 45 typedef struct { |
39 ngx_shm_zone_t *shm_zone; | 46 ngx_shm_zone_t *shm_zone; |
40 ngx_uint_t conn; | 47 ngx_uint_t conn; |
41 } ngx_http_limit_conn_limit_t; | 48 } ngx_http_limit_conn_limit_t; |
42 | 49 |
43 | 50 |
44 typedef struct { | 51 typedef struct { |
45 ngx_array_t limits; | 52 ngx_array_t limits; |
46 ngx_uint_t log_level; | 53 ngx_uint_t log_level; |
47 ngx_uint_t status_code; | 54 ngx_uint_t status_code; |
48 ngx_flag_t dry_run; | 55 ngx_flag_t dry_run; |
49 } ngx_http_limit_conn_conf_t; | 56 } ngx_http_limit_conn_conf_t; |
50 | 57 |
51 | 58 |
52 static ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, | 59 static ngx_rbtree_node_t *ngx_http_limit_conn_lookup(ngx_rbtree_t *rbtree, |
53 ngx_str_t *key, uint32_t hash); | 60 ngx_str_t *key, uint32_t hash); |
174 { | 181 { |
175 size_t n; | 182 size_t n; |
176 uint32_t hash; | 183 uint32_t hash; |
177 ngx_str_t key; | 184 ngx_str_t key; |
178 ngx_uint_t i; | 185 ngx_uint_t i; |
179 ngx_slab_pool_t *shpool; | |
180 ngx_rbtree_node_t *node; | 186 ngx_rbtree_node_t *node; |
181 ngx_pool_cleanup_t *cln; | 187 ngx_pool_cleanup_t *cln; |
182 ngx_http_limit_conn_ctx_t *ctx; | 188 ngx_http_limit_conn_ctx_t *ctx; |
183 ngx_http_limit_conn_node_t *lc; | 189 ngx_http_limit_conn_node_t *lc; |
184 ngx_http_limit_conn_conf_t *lccf; | 190 ngx_http_limit_conn_conf_t *lccf; |
213 | 219 |
214 r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_PASSED; | 220 r->main->limit_conn_status = NGX_HTTP_LIMIT_CONN_PASSED; |
215 | 221 |
216 hash = ngx_crc32_short(key.data, key.len); | 222 hash = ngx_crc32_short(key.data, key.len); |
217 | 223 |
218 shpool = (ngx_slab_pool_t *) limits[i].shm_zone->shm.addr; | 224 ngx_shmtx_lock(&ctx->shpool->mutex); |
219 | 225 |
220 ngx_shmtx_lock(&shpool->mutex); | 226 node = ngx_http_limit_conn_lookup(&ctx->sh->rbtree, &key, hash); |
221 | |
222 node = ngx_http_limit_conn_lookup(ctx->rbtree, &key, hash); | |
223 | 227 |
224 if (node == NULL) { | 228 if (node == NULL) { |
225 | 229 |
226 n = offsetof(ngx_rbtree_node_t, color) | 230 n = offsetof(ngx_rbtree_node_t, color) |
227 + offsetof(ngx_http_limit_conn_node_t, data) | 231 + offsetof(ngx_http_limit_conn_node_t, data) |
228 + key.len; | 232 + key.len; |
229 | 233 |
230 node = ngx_slab_alloc_locked(shpool, n); | 234 node = ngx_slab_alloc_locked(ctx->shpool, n); |
231 | 235 |
232 if (node == NULL) { | 236 if (node == NULL) { |
233 ngx_shmtx_unlock(&shpool->mutex); | 237 ngx_shmtx_unlock(&ctx->shpool->mutex); |
234 ngx_http_limit_conn_cleanup_all(r->pool); | 238 ngx_http_limit_conn_cleanup_all(r->pool); |
235 | 239 |
236 if (lccf->dry_run) { | 240 if (lccf->dry_run) { |
237 r->main->limit_conn_status = | 241 r->main->limit_conn_status = |
238 NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN; | 242 NGX_HTTP_LIMIT_CONN_REJECTED_DRY_RUN; |
249 node->key = hash; | 253 node->key = hash; |
250 lc->len = (u_char) key.len; | 254 lc->len = (u_char) key.len; |
251 lc->conn = 1; | 255 lc->conn = 1; |
252 ngx_memcpy(lc->data, key.data, key.len); | 256 ngx_memcpy(lc->data, key.data, key.len); |
253 | 257 |
254 ngx_rbtree_insert(ctx->rbtree, node); | 258 ngx_rbtree_insert(&ctx->sh->rbtree, node); |
255 | 259 |
256 } else { | 260 } else { |
257 | 261 |
258 lc = (ngx_http_limit_conn_node_t *) &node->color; | 262 lc = (ngx_http_limit_conn_node_t *) &node->color; |
259 | 263 |
260 if ((ngx_uint_t) lc->conn >= limits[i].conn) { | 264 if ((ngx_uint_t) lc->conn >= limits[i].conn) { |
261 | 265 |
262 ngx_shmtx_unlock(&shpool->mutex); | 266 ngx_shmtx_unlock(&ctx->shpool->mutex); |
263 | 267 |
264 ngx_log_error(lccf->log_level, r->connection->log, 0, | 268 ngx_log_error(lccf->log_level, r->connection->log, 0, |
265 "limiting connections%s by zone \"%V\"", | 269 "limiting connections%s by zone \"%V\"", |
266 lccf->dry_run ? ", dry run," : "", | 270 lccf->dry_run ? ", dry run," : "", |
267 &limits[i].shm_zone->shm.name); | 271 &limits[i].shm_zone->shm.name); |
283 } | 287 } |
284 | 288 |
285 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | 289 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, |
286 "limit conn: %08Xi %d", node->key, lc->conn); | 290 "limit conn: %08Xi %d", node->key, lc->conn); |
287 | 291 |
288 ngx_shmtx_unlock(&shpool->mutex); | 292 ngx_shmtx_unlock(&ctx->shpool->mutex); |
289 | 293 |
290 cln = ngx_pool_cleanup_add(r->pool, | 294 cln = ngx_pool_cleanup_add(r->pool, |
291 sizeof(ngx_http_limit_conn_cleanup_t)); | 295 sizeof(ngx_http_limit_conn_cleanup_t)); |
292 if (cln == NULL) { | 296 if (cln == NULL) { |
293 return NGX_HTTP_INTERNAL_SERVER_ERROR; | 297 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
387 static void | 391 static void |
388 ngx_http_limit_conn_cleanup(void *data) | 392 ngx_http_limit_conn_cleanup(void *data) |
389 { | 393 { |
390 ngx_http_limit_conn_cleanup_t *lccln = data; | 394 ngx_http_limit_conn_cleanup_t *lccln = data; |
391 | 395 |
392 ngx_slab_pool_t *shpool; | |
393 ngx_rbtree_node_t *node; | 396 ngx_rbtree_node_t *node; |
394 ngx_http_limit_conn_ctx_t *ctx; | 397 ngx_http_limit_conn_ctx_t *ctx; |
395 ngx_http_limit_conn_node_t *lc; | 398 ngx_http_limit_conn_node_t *lc; |
396 | 399 |
397 ctx = lccln->shm_zone->data; | 400 ctx = lccln->shm_zone->data; |
398 shpool = (ngx_slab_pool_t *) lccln->shm_zone->shm.addr; | |
399 node = lccln->node; | 401 node = lccln->node; |
400 lc = (ngx_http_limit_conn_node_t *) &node->color; | 402 lc = (ngx_http_limit_conn_node_t *) &node->color; |
401 | 403 |
402 ngx_shmtx_lock(&shpool->mutex); | 404 ngx_shmtx_lock(&ctx->shpool->mutex); |
403 | 405 |
404 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0, | 406 ngx_log_debug2(NGX_LOG_DEBUG_HTTP, lccln->shm_zone->shm.log, 0, |
405 "limit conn cleanup: %08Xi %d", node->key, lc->conn); | 407 "limit conn cleanup: %08Xi %d", node->key, lc->conn); |
406 | 408 |
407 lc->conn--; | 409 lc->conn--; |
408 | 410 |
409 if (lc->conn == 0) { | 411 if (lc->conn == 0) { |
410 ngx_rbtree_delete(ctx->rbtree, node); | 412 ngx_rbtree_delete(&ctx->sh->rbtree, node); |
411 ngx_slab_free_locked(shpool, node); | 413 ngx_slab_free_locked(ctx->shpool, node); |
412 } | 414 } |
413 | 415 |
414 ngx_shmtx_unlock(&shpool->mutex); | 416 ngx_shmtx_unlock(&ctx->shpool->mutex); |
415 } | 417 } |
416 | 418 |
417 | 419 |
418 static ngx_inline void | 420 static ngx_inline void |
419 ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool) | 421 ngx_http_limit_conn_cleanup_all(ngx_pool_t *pool) |
435 ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data) | 437 ngx_http_limit_conn_init_zone(ngx_shm_zone_t *shm_zone, void *data) |
436 { | 438 { |
437 ngx_http_limit_conn_ctx_t *octx = data; | 439 ngx_http_limit_conn_ctx_t *octx = data; |
438 | 440 |
439 size_t len; | 441 size_t len; |
440 ngx_slab_pool_t *shpool; | |
441 ngx_rbtree_node_t *sentinel; | |
442 ngx_http_limit_conn_ctx_t *ctx; | 442 ngx_http_limit_conn_ctx_t *ctx; |
443 | 443 |
444 ctx = shm_zone->data; | 444 ctx = shm_zone->data; |
445 | 445 |
446 if (octx) { | 446 if (octx) { |
455 &shm_zone->shm.name, &ctx->key.value, | 455 &shm_zone->shm.name, &ctx->key.value, |
456 &octx->key.value); | 456 &octx->key.value); |
457 return NGX_ERROR; | 457 return NGX_ERROR; |
458 } | 458 } |
459 | 459 |
460 ctx->rbtree = octx->rbtree; | 460 ctx->sh = octx->sh; |
461 ctx->shpool = octx->shpool; | |
461 | 462 |
462 return NGX_OK; | 463 return NGX_OK; |
463 } | 464 } |
464 | 465 |
465 shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; | 466 ctx->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr; |
466 | 467 |
467 if (shm_zone->shm.exists) { | 468 if (shm_zone->shm.exists) { |
468 ctx->rbtree = shpool->data; | 469 ctx->sh = ctx->shpool->data; |
469 | 470 |
470 return NGX_OK; | 471 return NGX_OK; |
471 } | 472 } |
472 | 473 |
473 ctx->rbtree = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_t)); | 474 ctx->sh = ngx_slab_alloc(ctx->shpool, sizeof(ngx_http_limit_conn_shctx_t)); |
474 if (ctx->rbtree == NULL) { | 475 if (ctx->sh == NULL) { |
475 return NGX_ERROR; | 476 return NGX_ERROR; |
476 } | 477 } |
477 | 478 |
478 shpool->data = ctx->rbtree; | 479 ctx->shpool->data = ctx->sh; |
479 | 480 |
480 sentinel = ngx_slab_alloc(shpool, sizeof(ngx_rbtree_node_t)); | 481 ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel, |
481 if (sentinel == NULL) { | 482 ngx_http_limit_conn_rbtree_insert_value); |
483 | |
484 len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len; | |
485 | |
486 ctx->shpool->log_ctx = ngx_slab_alloc(ctx->shpool, len); | |
487 if (ctx->shpool->log_ctx == NULL) { | |
482 return NGX_ERROR; | 488 return NGX_ERROR; |
483 } | 489 } |
484 | 490 |
485 ngx_rbtree_init(ctx->rbtree, sentinel, | 491 ngx_sprintf(ctx->shpool->log_ctx, " in limit_conn_zone \"%V\"%Z", |
486 ngx_http_limit_conn_rbtree_insert_value); | |
487 | |
488 len = sizeof(" in limit_conn_zone \"\"") + shm_zone->shm.name.len; | |
489 | |
490 shpool->log_ctx = ngx_slab_alloc(shpool, len); | |
491 if (shpool->log_ctx == NULL) { | |
492 return NGX_ERROR; | |
493 } | |
494 | |
495 ngx_sprintf(shpool->log_ctx, " in limit_conn_zone \"%V\"%Z", | |
496 &shm_zone->shm.name); | 492 &shm_zone->shm.name); |
497 | 493 |
498 return NGX_OK; | 494 return NGX_OK; |
499 } | 495 } |
500 | 496 |