NJet深层清理Lua代码

简介

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 表中,都将会把数组中列出的所有模块名进行清理。例如:

rust 复制代码
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

ini 复制代码
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/

lua 复制代码
local _M={}

function _M.run()
   njt.say("in mylib, init version")
end

return _M

访问 /luatest

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

vbnet 复制代码
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

lua 复制代码
local _M={}

function _M.run()
   njt.say("in mylib, here is the updated version")
end

return _M

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

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

说明

  • NJet v3.3.1 版本开始支持该API
  • 模块名称与文件路径关系是Lua的标准机制,在配置的package_path中依次搜索,将点号(.)替换为目录分隔符, 添加.lua扩展名,require("a.b") 会查找 搜索路径下的 a/b.lua
相关推荐
你才是臭弟弟19 分钟前
Nginx部署前后端
运维·nginx
红黑色的圣西罗1 小时前
Lua和C#交互探究记录
c#·lua·交互
_下雨天.10 小时前
Nginx性能调优与深度监控
运维·nginx
weiwx8311 小时前
Nginx location 和 proxy_pass 配置详解
服务器·网络·nginx
向往着的青绿色19 小时前
雷池(SafeLine)社区版免费部署教程|从环境检查到防护实操全流程
网络·计算机网络·nginx·网络安全·容器·网络攻击模型·信息与通信
@大迁世界1 天前
6 款轻量级 CLI 工具,取代了我臃肿的开发软件
开发语言·lua
困惑阿三1 天前
全栈服务器运维终极备忘录
运维·服务器·nginx·pm2
无籽西瓜a1 天前
Docker 环境下 Redis Lua 脚本部署与执行
redis·docker·lua
PascalMing1 天前
告别 Nginx!ASP.NET Core 实现多域名 Vue 静态服务与代理转发
vue.js·nginx·asp.net
深念Y1 天前
Nginx和Spring Cloud Gateway
运维·服务器·网络·网关·nginx·spring cloud·微服务