lua语言:模块热更新方案

嚴華  金牌会员 | 2024-5-29 22:01:14 | 显示全部楼层 | 阅读模式
打印 上一主题 下一主题

主题 916|帖子 916|积分 2748

我们知道,lua通过package模块来管理模块的搜索和加载,当利用require初次成功加载一个模块后,模块(Table)会被存储在package.loaded中,之后的require行为会直接获取到已加载的模块缓存。
怎样在步伐执行时,将对模块文件的修改热更新到步伐中,同时确保运行时状态的正常。这是项目开辟中常见的机制需求,这里给出一个遍历模块键值更换更新的方案:将新文件利用loadfile加载进内存,遍历原Table,根据key匹配value进行更换更新。
方案需要支持对运行时状态数据的继续。
模块在内存中以Table类型存在,我们把更新前的模块称为mod,在内存中的Table称为old_t,把新加载进内存的Table称为new_t。old_t被package管理:
  1. registry.package = {
  2.         loaded = {
  3.                     mod = old_t
  4.                     -- ...
  5.           }
  6. }
复制代码
将修改后的模块文件利用loadfile加载进内存(没有内置的缓存机制,每次编译),遍历将old_t的键值更换为new_t,实现模块的更新:
  1. -- load module file
  2. local new_t
  3. if package.loaded[mod] then
  4.     local filename = package.searchpath(mod, package.path)
  5.     local f, err = loadfile(filename)
  6.     if not f then
  7.                     assert(false, string.format("loadfile err=%s", err))
  8.           end
  9.     new_t = f()
  10. end
  11. -- release old value
  12. local keys = table.allkeys(old_t)
  13. for _, k in ipairs(keys) do
  14.           old_t[k] = nil
  15. end
  16. -- update new value
  17. for k, v in pairs(new_t) do
  18.           old_t[k] = v
  19. end
复制代码
运行时状态数据的处理
我们可以约定:

  • 需要继续的数据界说在模块的域内;
  • 模块提供release方法用于处理并收集原Table中需要继续的内存数据;
  • 模块提供onload方法用于将原Table的运行时数据继续到新的模块内存中
  1. local context, inherts
  2. local old_t = package.loaded[mod]
  3. if old_t and new_t then
  4.     if old_t._release then
  5.         context, inherts = old_t._release(old_t)
  6.     end
  7. end
  8. -- inhert old_t runtime
  9. if context and inherts then
  10.     for _, key in ipairs(inherts) do
  11.                     new_t[key] = old_t[key]
  12.           end
  13. end
复制代码
给出一个符合上述热更新规范的模块设计demo:
  1. local context = {} -- TODO logic agent context
  2. local logic = {
  3.     _name = "logic",
  4.     _inherit = { "_runtime" },
  5.     _release = function(self)
  6.         return context, self._inherit
  7.     end,
  8.     _onload = function(self, _context)
  9.         print(string.format("run reload on mod %s", self._name))
  10.         self._runtime._RELOAD_VERSION = self._runtime._RELOAD_VERSION + 1
  11.     end,
  12.     _runtime = {
  13.         _RELOAD_VERSION = 1
  14.     },
  15.     _hotfixver = function(self)
  16.         print("reload version:", self._runtime._RELOAD_VERSION)
  17.     end
  18. }
  19. function logic.callfunc()
  20.     print("run callfunc. [logic]")
  21. end
  22. return logic
复制代码
当我修改本地模块文件将callfunc函数界说为:
  1. function logic.callfunc()
  2.     print("run callfunc. [logic_v2]")
  3. end
复制代码
执行:
  1. local logic = require "logic"
  2. local reload = require "reload"
  3. -- old_t
  4. logic.callfunc()
  5. reload("logic")
  6. -- new_t
  7. logic.callfunc()
  8. logic:_hotfixver()
复制代码
从输出效果可以看出,callfunc被正确更新为修改后的函数,切runtime数据被正确继续;
  1. linxx@linxx-MacBookAir hotfix % lua tsreload.lua
  2. run callfunc. [logic]
  3. run reload on mod logic.
  4. run callfunc. [logic_v2]
  5. reload version:        2
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

嚴華

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表