Openresty 随手记

Openresty 执行阶段

我们可以在 init_by_lua 中写一些启动配置,比如读取配置文件等。之后 nginx 会 fork 出若干 worker 进程。由于 fork 的性质,worker 中拥有父进程(master 进程)的数据拷贝。所以依然能够访问到这些在 init_by_lua 中加载的变量。但是此时你修改变量的值并不会影响到其他的 worker

全局变量是十分危险的,尽量不要这么做,替代的方法是将其用 module 作为 namespace 进行包裹

-- init_by_lua
local setting = require("setting")  
setting:load(path)

-- setting.lua
local lyaml = require("lyaml")


local _M = {}


local function flatten(data)  
    -- load the setting automaticlly
    local result = {}
    for k, v in pairs(data) do
        if type(v) == 'table' then
            result[k] = flatten(v)
      else
            result[k] = v
        end
    end
    return result
end


function _M:load(filepath)  
    local f = io.open(filepath)
    local content = f:read("*all")
    f:close()
    local config = lyaml.load(content)
    self.config = flatten(config)
end


return setmetatable(_M, {__index = function(self, attr)  
    return self.config[attr]
end})  

这样需要使用 setting 时, require 一下即可

另外只有在 init_by_luainit_worker_by_lua 中定义的全局变量才会在之后的执行阶段中访问到。其他阶段中,Openresty 会隔离每次请求创建的全局变量。也就是说你在 access_by_lua 中创建的全局变量,并不是真正的全局变量,在 content_by_lua 中是访问不到的

当关闭 lua_code_cache 时,每一个 ngx_lua 处理的请求都将运行在一个独立的 Lua VM 中,这意味着 init_by_lua init_worker_by_lua 都会重新执行。生产环境下 lua_code_cache 应当启用。这时 require 的模块将被缓存,仅加载一次。如果被 require 的模块中创建了全局变量,便会产生问题。因为全局变量仅在当前的上下文(执行阶段)内有效。并且这是默认开启的,如果强行要 off,会有警告

nginx: [alert] lua_code_cache is off; this will hurt performance in /opt/openresty/nginx/conf/nginx.conf:10  

如果想要在不同的执行阶段中共享变量, 可以借助 ngx.ctx,它能存放各种类型

如果想要在不同的 worker 间共享变量,可以使用 ngx.sharedict,这是一个 shm。不过遗憾的是,只能接受 number string boolean 三种类型

还有一个问题是 lua_code_cache on 下模块被缓存,导致模块中的 local 变量在请求间共享,比如说,A 请求调用了模块中的函数 change()

local _M = {}  
local count = 1  
function _M.change()  
    ngx.say(count)
    count = 2333
    ngx.sleep(10)
end  
return _M  

紧接着 B 请求也调用了此函数,会发现一个 response 为 1,另一个为 2333。这种问题和竞态问题有些相似