我們知道,lua通過package模塊來管理模塊的搜索和載入,當使用require首次成功載入一個模塊後,模塊(Table)會被存儲在package.loaded中,之後的require行為會直接獲取到已載入的模塊緩存。 如何在程式執行時,將對模塊文件的修改熱更新到程式中,同時確保運行時狀態的正常。 ...
我們知道,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[mod] 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[k] = nil
end
-- update new value
for k, v in pairs(new_t) do
old_t[k] = v
end
運行時狀態數據的處理
我們可以約定:
-
需要繼承的數據定義在模塊的域內;
-
模塊提供release方法用於處理並收集原Table中需要繼承的記憶體數據;
-
模塊提供onload方法用於將原Table的運行時數據繼承到新的模塊記憶體中
local context, inherts
local old_t = package.loaded[mod]
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[key] = old_t[key]
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. [logic]")
end
return logic
當我修改本地模塊文件將callfunc函數定義為:
function logic.callfunc()
print("run callfunc. [logic_v2]")
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. [logic]
run reload on mod logic.
run callfunc. [logic_v2]
reload version: 2
本文來自博客園,作者:linxx-,轉載請註明原文鏈接:https://www.cnblogs.com/linxx-/p/18221342