写点什么

NJet 深层清理 Lua 代码

作者:通明湖
  • 2025-09-04
    北京
  • 本文字数:1942 字

    阅读完需:约 6 分钟

简介

NJet 集成了 Lua 的运行环境,在不配置 lua_code_cache off; 的情况下,worker 收到请求时,会从 context 中获取已经创建的 Lua VM (第一次执行 Lua 相关代码时创建)。Lua VM 中, package.loaded 是一个核心的 Lua 表,它扮演着 Lua 模块缓存的角色。当使用 require(“module_name”) 加载一个 Lua 模块时,Lua 解释器会首先检查 package.loaded 表中是否已经存在名为 module_name 的模块。如果存在,它会直接返回已缓存的模块值;如果不存在,它才会去文件系统(根据 package.path)查找、加载并执行该模块文件,然后将其返回值存储到 package.loaded 中,供后续调用。


在配置了多个 worker 进程时, 每个 worker 进程都拥有自己独立的 LuaJIT 虚拟机实例 (Lua State)。这意味着每个 worker 进程的 Lua 虚拟机都有自己独立的 package.loaded 表,它们之间互不影响。有可能造成部分 worker 进程缓存了模块,部分进程没有对模块进行缓存。


当更新了磁盘上的 Lua 模块文件时,例如 mylib.lua, package.loaded 中已经缓存了旧版本的 mylib 的 worker 进程将执行旧版的代码,而未缓存该模块的部分 worker 进程收到请求时将直接使用新版本,这样照成了各个 worker 的行为不一致。并且那些被间接 require 的模块(即主 Lua 脚本 require 了 A,而 A 又 require 了 B),Lua 自身也没有机制去追踪这种深层次的依赖关系并自动刷新。


动态清除方案

NJet 提供了完善的动态配置框架,通过 NJet Copilot 提供的消息机制,使用控制面提供的 Restful API 可以对进行了动态改造的模块进行实时的配置修改,而不需要触发配置文件的全量重加载或重启。


使用相同的动态配置机制,NJet 解决了上文提及的模块缓存清除问题。NJet 控制面提供了配置 URL “/api/v1/config/http_lua_package_clean” (需要加载 http 动态 lua 模块 njt_http_dyn_lua_module.so, 将需要清除缓存的模块名称,使用 JSON 的字符串数据 PUT 到该接口,所有 Worker 进程 Lua VM 的 package.loaded 表中,都将会把数组中列出的所有模块名进行清理。例如:


curl -X 'PUT' -d '["mylib", "mylib.util", "mylib.common"]' 'http://localhost:8081/api/v1/config/http_lua_package_clean'NJet 内置的 swagger 文档也进行了相应的更新,可以通过 swagger 提供的页面在浏览器中进行修改。


测试验证

Njet 中加载 lua 及动态 lua 模块,并配置一个静态 location


helper ctrl /usr/local/njet/modules/njt_helper_ctrl_module.so /usr/local/njet/conf/njet_ctrl.conf;helper broker /usr/local/njet/modules/njt_helper_broker_module.so conf/mqtt.conf;load_module /usr/local/njet/modules/njt_http_lua_module.so;load_module /usr/local/njet/modules/njt_http_dyn_lua_module.so;
worker_processes auto;cluster_name njet;node_name node1;error_log logs/error.log info;events { worker_connections 1024;}
http { include mime.types; lua_package_path "$prefix/lualib/lib/?.lua;/usr/local/njet/modules/?.lua;$prefix/apps/?.lua;;"; lua_package_cpath "$prefix/lualib/clib/?.so;;";
server { listen 80; location / { root html; } location /luatest { content_by_lua_block { local mylib = require("mylib") mylib.run() } } }}
复制代码


}}/luatest 中调用了mylib包中的 run函数, mylilb.lua 放在 lua_package_path 指定的其中一个路径中,如 $prefix/apps/
local _M={}
function _M.run()njt.say("in mylib, init version")end
return _M
复制代码


访问 /luatest



使用动态 lua, 修改 /luatest 中的 content_by_lua_block 内容

curl -X 'PUT'
'http://localhost:8081/api/v1/config/http_lua'
-H 'accept: application/json'
-H 'Content-Type: application/json'
-d '{"servers": [{"listens": ["0.0.0.0:80"],"serverNames": [""],"locations": [{"location": "/","lua": {}},{"location": "/luatest","lua": {"content_by": " njt.say("before mylib run") \n local mylib = require("mylib")\n mylib.run()\n njt.say("after mylib run") "}}]}]}'
复制代码

同时修改 mylib.lua

local _M={}
function _M.run()njt.say("in mylib, here is the updated version")end
return _M
复制代码



再次访问 /luatest, 动态 lua 的改动已生效,但是引用的 package 改动不生效


使用 NJet 控制面提供的 lua package 缓存清除机制

说明

  1. NJet v3.3.1 版本开始支持该 API

  2. 模块名称与文件路径关系是 Lua 的标准机制,在配置的 package_path 中依次搜索,将点号(.)替换为目录分隔符, 添加.lua 扩展名,require(“a.b”) 会查找 搜索路径下的 a/b.lua

发布于: 刚刚阅读数: 4
用户头像

通明湖

关注

让应用永远在线! 2022-10-13 加入

持续科技创新,信创应用交付领域的排头兵

评论

发布
暂无评论
NJet深层清理Lua代码_通明湖_InfoQ写作社区