Zone 컨텍스트 구조체
한 개의 limit_req_zone에 대한 런타임 정보를 담는 구조체다. Nginx 설정 파일에서 정의한 정보(rate, key, zone 크기 등)를 워커 프로세스가 실제 요청을 처리할 때 사용할 수 있도록 저장한다. 공유 메모리, 메모리 풀, 처리 속도 등 요청 처리에 필요한 모든 자원을 하나로 모아둔다.
구조체 정의
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/s는 10000으로 저장된다. 이는 밀리초 단위 계산에서 소수점 연산 없이 정수 연산만으로 정확한 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;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 필드를 증가시켜 메모리에서 제거되지 않도록 보호한다.
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에 저장하고 참조 카운트를 증가시킨다.
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를 최종 업데이트한 후 참조를 해제한다.