嚴華 发表于 2024-5-29 22:01:14

lua语言:模块热更新方案

我们知道,lua通过package模块来管理模块的搜索和加载,当利用require初次成功加载一个模块后,模块(Table)会被存储在package.loaded中,之后的require行为会直接获取到已加载的模块缓存。
怎样在步伐执行时,将对模块文件的修改热更新到步伐中,同时确保运行时状态的正常。这是项目开辟中常见的机制需求,这里给出一个遍历模块键值更换更新的方案:将新文件利用loadfile加载进内存,遍历原Table,根据key匹配value进行更换更新。
方案需要支持对运行时状态数据的继续。
模块在内存中以Table类型存在,我们把更新前的模块称为mod,在内存中的Table称为old_t,把新加载进内存的Table称为new_t。old_t被package管理:
registry.package = {
        loaded = {
                    mod = old_t
                    -- ...
        }
}将修改后的模块文件利用loadfile加载进内存(没有内置的缓存机制,每次编译),遍历将old_t的键值更换为new_t,实现模块的更新:
-- load module file
local new_t
if package.loaded then
    local filename = package.searchpath(mod, package.path)
    local f, err = loadfile(filename)
    if not f then
                    assert(false, string.format("loadfile err=%s", err))
        end
    new_t = f()
end

-- release old value
local keys = table.allkeys(old_t)
for _, k in ipairs(keys) do
        old_t = nil
end
-- update new value
for k, v in pairs(new_t) do
        old_t = v
end运行时状态数据的处理
我们可以约定:

[*]需要继续的数据界说在模块的域内;
[*]模块提供release方法用于处理并收集原Table中需要继续的内存数据;
[*]模块提供onload方法用于将原Table的运行时数据继续到新的模块内存中
local context, inherts
local old_t = package.loaded
if old_t and new_t then
    if old_t._release then
      context, inherts = old_t._release(old_t)
    end
end
-- inhert old_t runtime
if context and inherts then
    for _, key in ipairs(inherts) do
                    new_t = old_t
        end
end给出一个符合上述热更新规范的模块设计demo:
local context = {} -- TODO logic agent context

local logic = {
    _name = "logic",
    _inherit = { "_runtime" },
    _release = function(self)
      return context, self._inherit
    end,
    _onload = function(self, _context)
      print(string.format("run reload on mod %s", self._name))
      self._runtime._RELOAD_VERSION = self._runtime._RELOAD_VERSION + 1
    end,
    _runtime = {
      _RELOAD_VERSION = 1
    },
    _hotfixver = function(self)
      print("reload version:", self._runtime._RELOAD_VERSION)
    end
}

function logic.callfunc()
    print("run callfunc. ")
end

return logic当我修改本地模块文件将callfunc函数界说为:
function logic.callfunc()
    print("run callfunc. ")
end执行:
local logic = require "logic"
local reload = require "reload"

-- old_t
logic.callfunc()

reload("logic")

-- new_t
logic.callfunc()
logic:_hotfixver()从输出效果可以看出,callfunc被正确更新为修改后的函数,切runtime数据被正确继续;
linxx@linxx-MacBookAir hotfix % lua tsreload.lua
run callfunc.
run reload on mod logic.
run callfunc.
reload version:        2
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: lua语言:模块热更新方案