Skip to Content

Red-Black Tree와 LRU Queue

Nginx의 ngx_http_limit_req_module 모듈은 클라이언트 요청 빈도를 제한하기 위해 Red-Black Tree와 LRU Queue 두 가지 자료구조를 함께 사용한다. 이 두 자료구조는 서로 다른 목적을 가지고 있으며, 하나의 공유 메모리 영역에서 공존한다.

공유 컨텍스트 구조체

src/http/modules/ngx_http_limit_req_module.c
typedef struct { ngx_rbtree_t rbtree; ngx_rbtree_node_t sentinel; ngx_queue_t queue; } ngx_http_limit_req_shctx_t;

하나의 구조체에 두 자료구조가 모두 포함되어 있다. rbtree는 클라이언트를 빠르게 찾기 위한 인덱스이고, queue는 메모리가 부족할 때 어떤 노드를 제거할지 결정한다.

역할 분담

Red-Black Tree: 빠른 검색

클라이언트를 O(log n) 시간에 찾기 위한 인덱스 역할을 한다. 해시값과 실제 키로 정확한 노드를 빠르게 찾는다.

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) { // ... // Tree 검색 로직 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) { // 정확히 일치 // ... } node = (rc < 0) ? node->left : node->right; // 다른 후보 계속 탐색 } // ... }

LRU Queue: 메모리 관리

노드의 사용 시간 순서를 추적하여 메모리 부족 시 제거할 노드를 결정한다. 노드를 사용할 때마다 Queue 맨 앞으로 이동시켜 “최근 사용”을 표시하고, 메모리가 부족하면 맨 뒤(가장 오래 사용하지 않은 노드)부터 제거한다.

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) { // ... if (rc == 0) { // 정확히 일치 // Queue에서 노드를 맨 앞으로 이동 (최근 사용 표시) ngx_queue_remove(&lr->queue); ngx_queue_insert_head(&ctx->sh->queue, &lr->queue); // ... } }
src/http/modules/ngx_http_limit_req_module.c
static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n) { // ... while (n < 3) { // ... // LRU Queue 맨 뒤 노드 가져오기 (가장 오래 사용 안 함) q = ngx_queue_last(&ctx->sh->queue); lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue); // ... ngx_queue_remove(q); // LRU Queue에서 제거 // ... } }

참고 자료

Last updated on