记一次openresty踩坑
Posted 罗小胖的技术笔记
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了记一次openresty踩坑相关的知识,希望对你有一定的参考价值。
今天要说一下使用openresty踩到的坑,因为这个坑,导致了线上出现了很奇怪的现象。
OpenResty是一个基于nginx与Lua的高性能 Web 平台,具体可以去看https://moonbingbing.gitbooks.io/openresty-best-practices/content/openresty/install.html上面的具体东西。
背景
由于服务器压力过大,需要临时进行分流。分流的判断是交给openresty nginx模块去处理的,我们配置好项目id之后,根据项目id来设置下流的负载均衡ip。如果请求的项目id不在配置里,则保持原来逻辑,如果在配置里则使用新的负载均衡ip。
现象
local IP = adsconf.IP
local PORT = adsconf.PORT
local NEW_IP = adsconf.NEW_IP
function _M.get_response(...)
if projectid in adsconf.projectids then
IP = NEW_IP
end
...
end
这里我们声明了全局变量IP,NEW_IP。如果projectid在配置中,则让IP = NEW_IP。
我们潜意识里会认为lua是一个脚本语言,自然在openresty中每次执行都会把IP初始化为adsconf.IP。但是在实际应用中发现,每一个nginx worker都会只初始化一次这个IP变量,如果你不改变这个IP变量,那么不会有任何问题。一旦你进行修改,那么这个worker里IP变量的值,始终都是你改过之后的值。这里,一旦你设置了IP = NEW_IP之后,那么每次请求你所使用的值都是这个NEW_IP,不管你的判断是否生效。
调整代码
这个问题也很好解决,确定值不变的,放在前面完全没问题。但是一旦要做修改,你可能就需要在函数中传递参数,或者是把变量声明放到函数里面去。调整之后代码如下:
local PORT = adsconf.PORT
local NEW_IP = adsconf.NEW_IP
function _M.get_response(...)
local IP = adsconf.IP
if projectid in adsconf.projectids then
IP = NEW_IP
end
...
end
把IP的声明放到函数里就行了,这样每来一次请求,都会初始化这个IP值。
关于OpenResty 中 Lua 变量的范围
全局变量
在 OpenResty 里面,只有在 init_by_lua* 和 init_worker_by_lua* 阶段才能定义真正的全局变量。这是因为其他阶段里面,OpenResty 会设置一个隔离的全局变量表,以免在处理过程污染了其他请求。即使在上述两个可以定义全局变量的阶段,也尽量避免这么做。全局变量能解决的问题,用模块变量也能解决, 而且会更清晰、更干净。
模块变量
这里把定义在模块里面的变量称为模块变量。无论定义变量时有没有加 local,有没有通过 _M 把变量引用起来, 定义在模块里面的变量都是模块变量。由于 Lua VM 会把 require 进来的模块缓存到 package.loaded 表里,除非设置了 lua_code_cache off, 模块里定义的变量都会被缓存起来。而且重要的是,模块变量在每个请求中是共享的。模块变量的跨请求特性,可以有很多用途。比如在变量间共享值,或者在 init_worker_by_lua* 中初始化全局用到的数值。
我们使用的IP其实是一个模块变量,所以单独worker中,每次请求其实是共享这个变量的。
参考资料: https://moonbingbing.gitbooks.io/openresty-best-practices/ngx_lua/lua-variable-scope.html
以上是关于记一次openresty踩坑的主要内容,如果未能解决你的问题,请参考以下文章