Hello!
2012/12/6 徐建 wrote:
我之前在google group里发了个“一个匪夷所思的bug,大家遇到过吗?”的帖子,今天复现了我在帖子说的bug。
我在那个帖子下回复,一直在报340的错误,所以只能给春哥发邮件了。
openresty google group 同时也是一个邮件列表,你可以发送一封空白邮件到邮箱
复现的程序有3个文件,server端是 nginx.conf和sync.lua,
sync.lua放在了openresty/nginx/vendor/sync.lua;client为dev_trans.php。
sync.lua和dev_trans.php里分别需要修改了redis的地址和server的ip。
线上时,sync.lua里将10判断为了<9, 一直到90才判断为>9, 然后100判断为了<99。
我用的redis版本是2.4.17, openresty版本为1.2.3.8,系统为64位的centos5.8.
附件为上面说的3个文件和nginx执行过程中的strace数据。这个bug是在我们的生产环境是在999999是出现的,线下频繁复现。
1. 我在我本地试了一下你的用例,注意到你用例中的 stamp、processed_key、processing_key 等变量存放的都是字符串类型的值。
这意味着你在用 <= 这样的运算符对它们进行比较时其实是按字符串语义进行比较的,而非数值语义,即在字符串比较规则下, "100" <=
"99"(因为二者的第一个字符分别是 "1" 和 "9",而 "1" 的 ASCII 码 49 要小于 "9" 的 ASCII 码 57)。
正是因为这个原因,在你的生产环境中, "1012348" 这样的字符串其实也小于 "999999",所以自然不会把新的 stamp 值(即
1012348)更新到 redis 中去,于是 redis 里的值就定格在了 999999.
最简单的修复方式是在对 stamp、processed_key、processing_key 这些变量进行初始化时使用 Lua 标准函数
tonumber 进行类型转换,即
local stamp = tonumber(uri_args['stamp'])
local processing_key = tonumber(res['processing'])
local processed_key = tonumber(res['processed'])
这样你用 <= 这样的运行符对它们进行比较时,就是期望中的数值比较语义了。
2. 另外,我还注意到你的用例中某些非出错的代码路径中,你未并调用 set_keepalive() 方法,这意味着对于这些代码路径,你的
redis 连接都不会被放入连接池中而是在请求结束时被直接关闭。
比如对于 if stamp <= processed_key then ... 和 if stamp <= processed_key
then ... 这两条代码路径,你就没有调用 set_keepalive() 进行善后。
3. 同时,建议你在对 resty.redis 对象的操作进行错误处理时把错误消息字符串(一般是第二个返回值 err)也记入日志,例如:
local res, err = fifo:htgetall(process_key)
if not res then
ngx.log(ngx.ERR, "hgetall ", process_key, " error when start
syncing: ", err)
ngx.exit(502)
end
这样方便你诊断问题。我同时注意到你未对 set_keepalive 调用的结果进行错误处理,建议总是加上。
4. 最后,我注意到你的用例中多次犯了下面这个错误:
ngx.status = 502
ngx.exit(ngx.HTTP_OK)
这两句其实等价于下面这一句
ngx.exit(200)
因为 ngx.HTTP_OK 就是 200,而 ngx.exit() 的这个参数会覆盖你先前对 ngx.status 的赋值(即 502 状态码)。正确的写法是
ngx.exit(502)
Best regards,
-agentzh