Nginx的限流功能主要通过ngx_http_limit_req_module
和ngx_http_limit_conn_module
两个核心模块实现。下面我将对这两个模块的源码进行详细分析。
该模块基于"漏桶算法"实现请求速率限制,主要数据结构和工作流程如下:
// 限流配置结构体
typedef struct {
ngx_http_complex_value_t key; // 限流key的生成规则
ngx_http_limit_req_conf_t *limit_req; // 限流配置
ngx_http_limit_req_ctx_t *ctx; // 限流上下文
} ngx_http_limit_req_loc_conf_t;
// 限流桶结构体
typedef struct {
u_char color;
u_char dummy;
u_short len;
ngx_queue_t queue;
ngx_msec_t last; // 最后请求时间
ngx_uint_t excess; // 当前超额请求数
u_char data[1]; // 键值数据
} ngx_http_limit_req_node_t;
static ngx_int_t ngx_http_limit_req_handler(ngx_http_request_t *r) {
// 获取配置
lrcf = ngx_http_get_module_loc_conf(r, ngx_http_limit_req_module);
// 生成限流key
if (ngx_http_complex_value(r, &lrcf->key, &key) != NGX_OK) {
return NGX_ERROR;
}
// 计算当前请求是否允许通过
rc = ngx_http_limit_req_account(r, lrcf->limit_req, lrcf->limit_req->rate,
&excess, &limit);
// 根据结果决定是否限流
if (rc == NGX_BUSY) {
ngx_log_error(lrcf->limit_req->log_level, r->connection->log, 0,
"limiting requests, excess: %ui.%03ui by zone \"%V\"",
excess / 1000, excess % 1000, &lrcf->limit_req->shm_zone->shm.name);
return lrcf->limit_req->status_code;
}
return NGX_DECLINED;
}
static ngx_int_t ngx_http_limit_req_account(ngx_http_request_t *r,
ngx_http_limit_req_conf_t *lrcf, ngx_uint_t rate, ngx_uint_t *ep, ngx_uint_t *lp)
{
now = ngx_current_msec;
ms = (ngx_msec_int_t) (now - lr->last);
// 计算漏桶中的水量(请求数)
excess = lr->excess - (ngx_uint_t) (rate * ms / 1000) + 1000;
if (excess < 0) {
excess = 0;
}
*ep = excess;
// 检查是否超过限制
if ((ngx_uint_t) excess > lrcf->burst) {
return NGX_BUSY;
}
// 更新漏桶状态
lr->excess = excess;
lr->last = now;
return NGX_OK;
}
该模块基于共享内存实现连接数限制,主要数据结构和工作流程如下:
// 连接限制配置
typedef struct {
ngx_shm_zone_t *shm_zone; // 共享内存区域
ngx_uint_t conn; // 最大连接数
} ngx_http_limit_conn_conf_t;
// 连接限制上下文
typedef struct {
ngx_atomic_t *counter; // 连接计数器
ngx_http_limit_conn_t *limit_conn; // 限制配置
} ngx_http_limit_conn_ctx_t;
static ngx_int_t ngx_http_limit_conn_handler(ngx_http_request_t *r) {
// 获取配置
lccf = ngx_http_get_module_loc_conf(r, ngx_http_limit_conn_module);
// 生成限制key
if (ngx_http_complex_value(r, &lccf->key, &key) != NGX_OK) {
return NGX_ERROR;
}
// 检查连接数
rc = ngx_http_limit_conn_account(r, lccf->shm_zone, &key, lccf->conn);
if (rc == NGX_HTTP_SERVICE_UNAVAILABLE) {
ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
"limiting connections by zone \"%V\"",
&lccf->shm_zone->shm.name);
return rc;
}
return NGX_DECLINED;
}
static ngx_int_t ngx_http_limit_conn_account(ngx_http_request_t *r,
ngx_shm_zone_t *shm_zone, ngx_str_t *key, ngx_uint_t conn)
{
ctx = shm_zone->data;
hash = ngx_crc32_short(key->data, key->len);
// 在共享内存中查找或创建节点
node = ngx_http_limit_conn_lookup(ctx->rbtree, key, hash);
if (node == NULL) {
// 创建新节点
node = ngx_slab_alloc_locked(ctx->shpool, sizeof(ngx_http_limit_conn_node_t));
node->key = *key;
node->hash = hash;
node->conn = 1;
ngx_rbtree_insert(&ctx->rbtree, &node->node);
} else {
// 已有节点,增加连接数
if (node->conn >= conn) {
return NGX_HTTP_SERVICE_UNAVAILABLE;
}
node->conn++;
}
return NGX_OK;
}
两个限流模块都使用共享内存来存储状态信息,关键实现如下:
// 共享内存初始化
static ngx_int_t ngx_http_limit_req_init_zone(ngx_shm_zone_t *shm_zone, void *data) {
ctx = shm_zone->data;
// 初始化红黑树(用于快速查找)
ngx_rbtree_init(&ctx->sh->rbtree, &ctx->sh->sentinel,
ngx_http_limit_req_rbtree_insert_value);
// 初始化LRU队列
ngx_queue_init(&ctx->sh->queue);
return NGX_OK;
}
// 共享内存清理
static void ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n) {
for ( ;; ) {
if (ngx_queue_empty(&ctx->sh->queue)) {
break;
}
// 从LRU队列尾部获取最久未使用的节点
q = ngx_queue_last(&ctx->sh->queue);
lr = ngx_queue_data(q, ngx_http_limit_req_node_t, queue);
// 检查是否过期
if (n++ != 0) {
ms = (ngx_msec_int_t) (now - lr->last);
ms = ngx_abs(ms);
if (ms < 60000) {
break;
}
}
// 从红黑树中移除并释放
ngx_queue_remove(q);
ngx_rbtree_delete(&ctx->sh->rbtree, &lr->node);
ngx_slab_free_locked(ctx->shpool, lr);
}
}
Nginx限流配置的解析过程:
static char *ngx_http_limit_req(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
// 解析limit_req指令
// 例如: limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
value = cf->args->elts;
// 解析zone名称和大小
if (ngx_strncmp(value[3].data, "zone=", 5) != 0) {
return NGX_CONF_ERROR;
}
// 解析rate值
if (ngx_strncmp(value[4].data, "rate=", 5) != 0) {
return NGX_CONF_ERROR;
}
// 创建共享内存zone
shm_zone = ngx_shared_memory_add(cf, &name, size, &ngx_http_limit_req_module);
return NGX_CONF_OK;
}
Nginx的限流模块通过精心设计的数据结构和算法实现了高效的请求控制和连接限制:
这些实现使得Nginx能够在高并发场景下提供稳定可靠的限流功能,同时保持极高的性能。