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
相关产品推荐
相关产品推荐

