主题: lua中require在ngx_lua环境中导入模块后后续无法访问问题。

通过 require 引用外部模块一般有 2 种写法。老的写法是:


require("xxx") 


这样会将模块命名空间表直接导入当前全局环境内;而新的写法是:


local xxx = require("xxx") 


这样的写法将模块命名空间表缓存在同名局部变量中,访问更快,也不会污染当前全局环境。但最重要的一点是:老的写法在 ngx_lua 中会出现模块导入后无法访问的现象!这是由 ngx_lua 实现原理决定的。ngx_lua 使用每请求一个 coroutine 的方式运行用户代码,coroutine 的全局环境是重新关联的,因此用户代码相当于运行在一个沙盒中,请求处理结束后用户代码产生的所有全局环境修改都会被舍弃,避免多个请求之间产生交叉影响,也降低了因滥用全局环境产生内存泄漏的风险。而 require 利用了全局共享的 package.loaded 表缓存已载入模块的数据,以达到避免重复加载模块的目的。很明显,这种结构必然会使首个请求中通过 require 注入全局环境的模块命名空间表在后续请求中无法访问,因为后续请求中 package.loaded 表内已经有之前加载模块的数据,故 require 不会再次将命名空间表注入当前全局环境,使得以后所有依赖于模块的操作都失败。

鉴于这一问题,我们推荐开发人员总是使用新的 require 写法(即使用局部变量缓存模块表),对于那些因为某种原因无法更新 require 写法的代码,可以通过在开始处理请求前清空 package.loaded 表中对应模块数据的方式强制加载模块并注入全局环境(注意每次都加载模块可能产生性能瓶颈!),例如:

package.loaded.xxx = nil 
require("xxx")