插件窝 干货文章 nginx限流模块源码分析

nginx限流模块源码分析

ngx limit http req 989    来源:    2025-04-27

Nginx限流模块源码分析

Nginx的限流功能主要通过ngx_http_limit_req_modulengx_http_limit_conn_module两个核心模块实现。下面我将对这两个模块的源码进行详细分析。

1. ngx_http_limit_req_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;
}

2. ngx_http_limit_conn_module (连接数限流模块)

基本工作原理

该模块基于共享内存实现连接数限制,主要数据结构和工作流程如下:

核心数据结构

// 连接限制配置
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;
}

3. 共享内存管理

两个限流模块都使用共享内存来存储状态信息,关键实现如下:

// 共享内存初始化
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);
    }
}

4. 性能优化点

  1. 红黑树查找:使用红黑树实现O(logN)时间复杂度的键值查找
  2. LRU队列:维护LRU队列用于快速淘汰过期数据
  3. 原子操作:使用原子操作保证共享内存访问的线程安全
  4. 内存池管理:通过slab内存池减少内存碎片

5. 配置解析

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的限流模块通过精心设计的数据结构和算法实现了高效的请求控制和连接限制:

  1. 请求限流:基于漏桶算法平滑限制请求速率
  2. 连接限流:基于共享内存和原子操作限制并发连接数
  3. 高效存储:使用红黑树+LRU队列实现快速查找和过期清理
  4. 分布式支持:通过共享内存支持多worker进程间的状态共享

这些实现使得Nginx能够在高并发场景下提供稳定可靠的限流功能,同时保持极高的性能。