Skip to Content

Zone 컨텍스트 구조체

한 개의 limit_req_zone에 대한 런타임 정보를 담는 구조체다. Nginx 설정 파일에서 정의한 정보(rate, key, zone 크기 등)를 워커 프로세스가 실제 요청을 처리할 때 사용할 수 있도록 저장한다. 공유 메모리, 메모리 풀, 처리 속도 등 요청 처리에 필요한 모든 자원을 하나로 모아둔다.

구조체 정의

src/http/modules/ngx_http_limit_req_module.c
typedef struct { ngx_http_limit_req_shctx_t *sh; ngx_slab_pool_t *shpool; /* integer value, 1 corresponds to 0.001 r/s */ ngx_uint_t rate; ngx_http_complex_value_t key; ngx_http_limit_req_node_t *node; } ngx_http_limit_req_ctx_t;

이 구조체는 한 개의 limit_req_zone에 대한 런타임 정보를 담는다. 요청이 들어오면 이 컨텍스트를 통해 공유 메모리에 접근하고, rate 계산을 수행하며, 클라이언트를 식별한다.

각 필드의 역할

1. sh: 공유 메모리 포인터

ngx_http_limit_req_shctx_t *sh; // Red-Black Tree와 LRU Queue를 담은 공유 메모리

모든 워커 프로세스가 클라이언트 상태를 공유하기 위한 메모리 영역을 가리킨다. 이 포인터를 통해 Red-Black Tree와 LRU Queue에 접근할 수 있으며, 여러 프로세스가 동일한 sh를 보므로 클라이언트별 요청 카운트가 정확하게 유지된다.

node = ctx->sh->rbtree.root; // Tree 루트 접근
q = ngx_queue_last(&ctx->sh->queue); // Queue 접근

2. shpool: 메모리 풀

ngx_slab_pool_t *shpool; // 동적 메모리 할당을 위한 Slab Allocator

공유 메모리 내에서 노드를 할당하고 해제하는 메모리 풀이다. Slab Allocator는 고정 크기 블록(64, 128, 256 bytes 등)을 사용해 메모리 단편화(fragmentation)를 최소화하고 O(1) 할당/해제 성능을 제공한다. 또한 shpool->mutex를 통해 멀티 프로세스 환경에서 동기화를 수행한다.

node = ngx_slab_alloc_locked(ctx->shpool, size); // 노드 할당
ngx_slab_free_locked(ctx->shpool, node); // 노드 해제

새 클라이언트 등록 시 ctx->shpool로 메모리를 할당하고, 만료된 노드는 해제한다.

3. rate: 처리 속도

ngx_uint_t rate; // 초당 요청 수 × 1000 (정수 연산을 위한 스케일링)

설정 파일의 rate 값을 1000배로 스케일링하여 저장한다. 예를 들어 rate=10r/s10000으로 저장된다. 이는 밀리초 단위 계산에서 소수점 연산 없이 정수 연산만으로 정확한 Leaky Bucket 계산을 가능하게 한다.

4. key: 클라이언트 식별자

ngx_http_complex_value_t key; // 동적으로 평가되는 클라이언트 식별 키

설정 파일에서 지정한 Nginx 변수(예: $binary_remote_addr)를 런타임에 평가하여 클라이언트를 식별한다. 요청마다 이 값을 계산하고 해시하여 Tree에서 노드를 검색한다.

설정 예시:

# IP 주소로 식별 limit_req_zone $binary_remote_addr zone=ip:10m rate=10r/s; # API 키로 식별 limit_req_zone $http_x_api_key zone=api:10m rate=100r/s;
src/http/modules/ngx_http_limit_req_module.c
static ngx_int_t ngx_http_limit_req_handler(ngx_http_request_t *r) { // ... // 요청마다 key 값을 동적으로 계산 if (ngx_http_complex_value(r, &ctx->key, &key) != NGX_OK) { ngx_http_limit_req_unlock(limits, n); return NGX_HTTP_INTERNAL_SERVER_ERROR; } // 계산된 key로 해시 생성 hash = ngx_crc32_short(key.data, key.len); // ... }

5. node: 현재 처리 중인 노드

ngx_http_limit_req_node_t *node; // lookup 함수에서 찾은 노드의 임시 참조

lookup 함수에서 찾은 클라이언트 노드를 임시로 저장한다. 여러 limit_req_zone을 순차적으로 체크할 때 각 zone의 노드를 보관하며, account 함수에서 이 노드의 excess를 최종 업데이트한다. 처리 중에는 count 필드를 증가시켜 메모리에서 제거되지 않도록 보호한다.

src/http/modules/ngx_http_limit_req_module.c
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) { // ... lr->count++; // 참조 카운트 증가 (메모리 제거 방지) ctx->node = lr; // ... }

lookup 함수에서 노드를 찾으면 ctx->node에 저장하고 참조 카운트를 증가시킨다.

src/http/modules/ngx_http_limit_req_module.c
static ngx_msec_t ngx_http_limit_req_account(ngx_http_limit_req_limit_t *limits, ngx_uint_t n, ngx_uint_t *ep, ngx_http_limit_req_limit_t **limit) { // ... lr = ctx->node; // ... excess 업데이트 lr->count--; // 참조 카운트 감소 ctx->node = NULL; // ... }

account 함수에서 노드의 excess를 최종 업데이트한 후 참조를 해제한다.

참고 자료

Last updated on