You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Nginx如何统计上游块中各上游IP的请求处理次数?

问题解决:Nginx上游服务器请求计数实现方案

一、如何在proxy_pass后执行ngx.location.capture

Nginx的请求处理是分阶段的,proxy_pass属于content阶段,要在其之后执行操作,有两种可靠方式:

1. 同步执行:使用post_action指令

该指令属于ngx_http_addition_module模块,需确保Nginx编译时包含此模块。它会在主请求(proxy_pass)完成后触发子请求,但会阻塞主请求响应,适合低并发场景。

配置示例:

upstream test {
    server ip1:port1;
    server ip1:port2;
    server ip1:port3;
    server ip1:port4;
}

location / {
    set $dst test;
    proxy_pass http://$dst;
    # 捕获实际转发的上游服务器地址
    set $upstream_server $upstream_addr;
    # 指定主请求完成后触发的内部子请求
    post_action /update-counter;
}

# 内部子请求,仅允许Nginx内部调用
location /update-counter {
    internal;
    content_by_lua_block {
        local upstream = ngx.var.upstream_server
        # 发起子请求更新数据库计数
        ngx.location.capture("/db-update", {
            args = { server = upstream }
        })
        # 可忽略子请求结果,不影响主请求响应
    }
}

# 数据库更新接口(内部访问)
location /db-update {
    internal;
    proxy_pass http://your-db-service/update-counter;
}

2. 异步执行:使用OpenResty的ngx.timer.at

这是高并发场景下的最优方案,通过异步定时器在后台执行数据库更新,不会阻塞主请求响应,需要使用OpenResty或编译了Lua模块的Nginx。

配置示例:

upstream test {
    server ip1:port1;
    server ip1:port2;
    server ip1:port3;
    server ip1:port4;
}

location / {
    set $dst test;
    proxy_pass http://$dst;
    content_by_lua_block {
        local upstream = ngx.var.upstream_addr
        # 0表示立即执行异步任务,不阻塞主请求
        local ok, err = ngx.timer.at(0, function(premature)
            if premature then return end
            # 发起子请求更新计数
            local res = ngx.location.capture("/db-update", {
                args = { server = upstream }
            })
            # 记录错误日志(可选)
            if not res then
                ngx.log(ngx.ERR, "计数更新失败: ", err)
            end
        end)
        if not ok then
            ngx.log(ngx.ERR, "定时器创建失败: ", err)
        end
    }
}

location /db-update {
    internal;
    proxy_pass http://your-db-service/update-counter;
}

二、其他实现请求计数的方案

1. 直接用Lua操作数据库

跳过子请求,在异步定时器中使用lua-resty-mysql直接操作数据库,减少转发开销,性能更高:

-- 替换定时器内的子请求代码
local mysql = require "resty.mysql"
local db, err = mysql:new()
if not db then
    ngx.log(ngx.ERR, "MySQL实例创建失败: ", err)
    return
end

db:set_timeout(1000)
local ok, err = db:connect{
    host = "your-db-host",
    port = 3306,
    database = "your-db-name",
    user = "db-user",
    password = "db-pass",
    charset = "utf8"
}

if not ok then
    ngx.log(ngx.ERR, "MySQL连接失败: ", err)
    return
end

-- 执行更新语句
local sql = string.format("UPDATE upstream_counters SET count = count + 1 WHERE server_addr = '%s'", upstream)
local res, err = db:query(sql)
if not res then
    ngx.log(ngx.ERR, "SQL执行失败: ", err)
end

-- 放回连接池
db:set_keepalive(10000, 100)

2. 日志解析批量更新

对实时性要求不高的场景,可通过Nginx日志记录上游地址,再用日志收集工具(如Filebeat+Logstash)解析日志,批量更新数据库,对Nginx性能影响极小:

# 在http块中定义自定义日志格式
log_format upstream_counter '$remote_addr [$time_local] "$request" $upstream_addr';
# 启用该日志
access_log /var/log/nginx/upstream_counter.log upstream_counter;

后续通过Logstash提取$upstream_addr字段,定时批量更新数据库即可。

内容的提问来源于stack exchange,提问作者Jahnavi latha

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 19:52:27