Lua resty_dogstatsd仅上报首个实例的问题排查求助
你的问题核心在于Nginx Lua模块的加载机制和resty_dogstatsd客户端的socket生命周期不匹配,咱们一步步拆解原因:
为什么单独require metrics模块会出错?
当你通过require('module.metrics')引入指标模块时,Lua的模块加载机制会把这个模块的代码只执行一次,并将结果缓存起来——也就是说,_statsd这个客户端实例是在Nginx启动(或者第一次请求触发模块加载)时创建的,之后所有请求都会复用这个已经存在的实例。
而resty_dogstatsd底层依赖的socket连接,在第一次请求完成后会被Nginx的连接池自动关闭(或者因超时等原因断开)。当后续请求再调用_statsd:increment()时,尝试使用已经关闭的socket,就会触发你看到的attempt to send data on a closed socket错误,自然无法正常上报指标。
为什么直接把代码写在start.lua里就正常?
因为你的Nginx配置用了rewrite_by_lua_file /etc/nginx/conf.d/start.lua;,这个指令会在每个请求到来时重新执行整个start.lua文件。也就是说,每次请求都会重新实例化resty_dogstatsd客户端,创建新的socket连接,完全避开了旧连接关闭的问题。
针对这个问题,有两种实用的修复方式,你可以根据场景选择:
方案1:每次调用指标方法时重新创建客户端实例
修改你的metrics模块,不要在模块加载时就初始化_statsd,而是在每个指标方法内部动态创建实例:
package.path = package.path .. ';../?.lua' local metrics = {} local resty_dogstatsd = require('resty_dogstatsd') local logger = require('module.utils.logger') function metrics.incMetric1 () logger.debug('Updating metric metric1') -- 每次调用时创建新的客户端实例 local _statsd = resty_dogstatsd.new({ statsd = { host = config.dataDog.host, port = config.dataDog.port, namespace = config.dataDog.namespace }, tags = config.dataDog.tags }) _statsd:increment('metric1', 1, 1) -- 顺便修正了你原代码里的语法错误 end return metrics;
这种方式简单直接,每个请求都会使用新的socket连接,彻底避免旧连接失效的问题。缺点是每次创建实例会有一点点性能开销,但对于大多数业务场景来说完全可以忽略。
方案2:检查连接状态,断开时自动重建
如果担心频繁创建实例的开销,可以封装一个方法,在调用前检查socket是否可用,不可用时再重新初始化:
package.path = package.path .. ';../?.lua' local metrics = {} local resty_dogstatsd = require('resty_dogstatsd') local logger = require('module.utils.logger') local _statsd = nil -- 封装获取可用客户端的工具方法 local function get_statsd_client() if not _statsd or (_statsd.socket and _statsd.socket:closed()) then _statsd = resty_dogstatsd.new({ statsd = { host = config.dataDog.host, port = config.dataDog.port, namespace = config.dataDog.namespace }, tags = config.dataDog.tags }) end return _statsd end function metrics.incMetric1 () logger.debug('Updating metric metric1') local statsd = get_statsd_client() statsd:increment('metric1', 1, 1) end return metrics;
这种方式既复用了可用的连接,又能在连接关闭时自动重建,平衡了性能和可靠性。
顺便修正你原代码里的小问题
你原metrics模块的_statsd:increment(metric1 1, 1)存在两处语法错误:
metric1应该加引号,变成'metric1'(否则会被当作变量解析)- 第一个参数和第二个参数之间缺少逗号,应该是
'metric1', 1, 1
这个错误虽然不是导致你问题的直接原因,但也会影响指标上报的正确性,记得修正。
内容的提问来源于stack exchange,提问作者Kousha

