1. 程式人生 > >Lua 指令碼重啟 機制

Lua 指令碼重啟 機制

不管是 現在開發中的遊戲服務端, 還是近期love2D 開發的前端, 都使用 Lua 做指令碼引擎, 需要涉及到 指令碼的修改和重啟. 第一種方法是 寫個封裝函式, 裡面進行對所有 lua 指令碼檔案的 require() 操作, 這就要求 :

1.對每個支援重新載入的檔案進行

package.loaded[ filename]  = nil
require( filename)

2.檔案載入要保持一定的順序, 以免造成資源的錯亂.

就當前使用 love2D 前端來看, 其實只有一個 "啟動"檔案: main.lua, 並在其內進行 各個子功能指令碼的 require 載入.如果在 重新載入時, 自動按照 main.lua 提供的

require(...) 順序進行自動載入就好了, 並且無需像上面的針對每個檔案編寫:

function reload_files()
    require( f1)
    require( f2)
    ...

end

整體目標有幾個:
1.無需靜態維護一個重新載入的檔案, 或函式, 進行編寫 各個指令碼檔案的 require() 進行重新載入;

2.能夠按照當前檔案中各個檔案的 順序進行載入, 即如果

--main.lua

require( "config")
require( "function")
require( "globals")
require( "gameplayer") require( "scene")

NeedReset = true
...

這種順序編寫main.lua( 或其他檔案), 都儘量保持 config > function > globals > gameplayer > scene 的順序進行重新載入;
3.能夠避免 "已被重新記載的檔案" 再次被重新載入;

4.能夠避免 巢狀遞迴載入;

5.能夠對 外部庫進行識別, 即 

require( "bit")

是在載入 "位操作"的 庫 bit.dll , 而不是 bit.lua, 不應該進行 巢狀載入;
6.能夠識別某些 "禁止重新載入"的檔案, 例如:

-- global.lua

require( "skill_cfg")
require( "effect_cfg")

g_object_list = {}

global.lua 檔案本身不能被 多次require(), 不然 g_object_list 全域性變數會被重置, 但又能夠不會影響 skill_cfg 和 effect_cfg 的重新載入;

7.應該要支援 "後序" 方式進行載入, 記載載入 main.lua 過程中, 應該現在遞迴載入完 子指令碼檔案:

require( "config")
require( "function")
require( "globals")
require( "gameplayer")
require( "scene")

然後在進行 載入 main.lua 的後序內容:

NeedReset = true
...

8.能夠 識別 檔案中的 require(...) 行.

大概這 8 點目標 和要求, 但對於第7點, 有個問題:

假設 重新載入 的 遞迴函式為

function recursive_reload( filename)

   package.loaded[ filename] = nil  
   require( filename ) end

並且main.lua 的內容簡單有如:

--main.lua

require( "config") 
require( "function") 
require( "globals") 
require( "gameplayer") 
require( "scene")

NeedReset = true

在 觸發重新載入的 入口中:

function main_reloader()  
    recursive_reload( "mian" ) 
end

呼叫 main_reloader() 進行重新載入的過程 展開將會如:

--先遞迴地使用 recursive_reload() 重新載入子檔案 

package.loaded[ 'config'] = nil 
require( 'config')

package.loaded[ 'function'] = nil 
require( 'function')

package.loaded[ 'globals'] = nil 
require( 'globals')

package.loaded[ 'gameplayer'] = nil 
require( 'gameplayer')

package.loaded[ 'scene'] = nil 
require( 'scene')

--再最後載入 main.lua 
package.loaded[ 'main'] = nil 
require( 'main') --但就在這個操作中, 還會涉及到巢狀的:  
require( "config")  
require( "function")  
require( "globals")  
require( "gameplayer")  
require( "scene")  

NeedReset = true

這 5 個 檔案不就會被 多次 require() 了嗎? 雖然 完整的 recursive_reload() 能夠防止 "顯示的" 重複require(),  但是不能禁止 "隱式的" require() 其實, 就算第二次的 "隱式" requre() 確實會呼叫, 但不會重新載入 實際的物理檔案, 見於 lua 開發手冊上:

require (modname) Loads the given module. 

The function starts by looking into the package.loaded table to determine whether modname is already loaded. 
If it is, then require returns the value stored at package.loaded[modname].
Otherwise, it tries to find a loader for the module.

即是說, 只要曾經載入了 檔案, 並在 package.loaded 內有記錄, 後序的 requre() 將會直接返回.

這 5 個 檔案不就會被 多次 require() 了嗎? 雖然 完整的 recursive_reload() 能夠防止 "顯示的" 重複require(),  但是不能禁止 "隱式的" require() 其實, 就算第二次的 "隱式" requre() 確實會呼叫, 但不會重新載入 實際的物理檔案, 見於 lua 開發手冊上:

require (modname) Loads the given module. The function starts by looking into the package.loaded table to determine whether modname is already loaded. If it is, then require returns the value stored at package.loaded[modname]. Otherwise, it tries to find a loader for the module.

即是說, 只要曾經載入了 檔案, 並在 package.loaded 內有記錄, 後序的 requre() 將會直接返回.

具體執行效果:

只是具體的實現程式碼:

-- 外部庫 登記
local package_list = {
    bit = true 
}

-- 全域性性質類/或禁止重新載入的檔案記錄
local ignored_file_list = {
    global = true ,
}

--已重新載入的檔案記錄
local loaded_file_list = {}

--檢視排版控制
function leading_tag( indent )
    -- body
    if indent < 1 then
        return ''
    else
        return string.rep( '    |',  indent - 1  ) .. '    '
    end
end

--關鍵遞迴重新載入函式
--filename 檔名
--indent   遞迴深度, 用於控制排版顯示
function recursive_reload( filename, indent )
    -- body
    if package_list[ filename] then 
        --對於 外部庫, 只進行重新載入, 不做遞迴子檔案
        --解除安裝舊檔案
        package.loaded[ filename] = nil

        --裝載信檔案
        require( filename )

        --標記"已被重新載入"
        loaded_file_list[ filename] = true

        print( leading_tag(indent) .. filename .. "... done" )
        return true
    end

    --普通檔案
    --進行 "已被重新載入" 檢測
    if loaded_file_list[ filename] then 
        print( leading_tag(indent) .. filename .. "...already been reloaded IGNORED" )
        return true
    end

    --讀取當前檔案內容, 以進行子檔案遞迴重新載入
    local file, err = io.open( filename..".lua" )
    if file == nil then 
        print( string.format( "failed to reaload file(%s), with error:%s", filename, err or "unknown" ) )
        return false
    end

    print( leading_tag(indent) .. filename .. "..." )

    --讀取每一行
    for line in file:lines() do 
        
        --識別 require(...)行, 正則表達? 模式匹配? 並拾取檔名 到 subFileName
        line = string.gsub( line, '%s', '' )
        local subFileName = nil 
        local ret = string.gsub( line, '^require%("(.+)"%)', function ( s ) subFileName = s end )

        if subFileName then
            --進行遞迴 
            local success = recursive_reload( subFileName, indent + 1 )
            if not success then 
                print( string.format( "failed to reload sub file of (%s)", filename ) )
                return false 
            end

        end
        
    end    


    -- "後序" 處理當前檔案...

    
    if ignored_file_list[ filename] then
        --忽略 "禁止被重新載入"的檔案
        print( leading_tag(indent) .. filename .. "... IGNORED" )
        return true
    else

        --解除安裝舊檔案
        package.loaded[ filename] = nil

        --裝載新檔案
        require( filename )

        --設定"已被重新載入" 標記
        loaded_file_list[ filename] = true
        print( leading_tag(indent) .. filename .. "... done" )
        return true
    end
end

--主入口函式
function reload_script_files()
    
    print( "[reload_script_files...]")

    loaded_file_list = {}

    --本專案是以 main.lua 為主檔案
    recursive_reload( "main", 0 )
    
    print( "[reload_script_files...done]")

    return "reload ok"
end

備註: 該機制只支援簡單檔案目錄

相關推薦

Lua 指令碼 機制

不管是 現在開發中的遊戲服務端, 還是近期love2D 開發的前端, 都使用 Lua 做指令碼引擎, 需要涉及到 指令碼的修改和重啟. 第一種方法是 寫個封裝函式, 裡面進行對所有 lua 指令碼檔案的 require() 操作, 這就要求 : 1.對每個支援重新載入的檔案進行 package.loa

kubelet版本升級引起的容器機制與參考解決方案_Kubernetes中文社群

背景 k8s能夠幫助我們的服務實現服務高可用,其提供的副本機制能夠有效的保證執行例項的副本數,從而當某個例項異常後服務可以重新被自動喚起,但在我們的生產環境中,某些特殊的服務(如廣告資金服務或計費服務)因服務重啟期間而導致的業務中斷,對業務請求的延時響應也是不可忽略的問題;而在kubelet的

swoole機制(轉載)

 1) sapi:可以簡單的理解為php引擎對外的一個統一介面,使得php可以和外部程式進行互動  2) php的生命週期中關鍵四個呼叫:MINT -> RINT -> RSHUTDOWN -> MSHUTDOWN  3)  fpm

Nginx 啟動指令碼/指令碼

第一步先執行命令關閉nginx sudo kill `cat /usr/local/nginx/logs/nginx.pid`第二步 vi /etc/init.d/nginx 輸入以下內容 #!/bin/sh # # nginx - this script sta

redis原子性讀寫操作之LUA指令碼和watch機制

最近在開發電商平臺的子系統——儲值卡系統,系統核心業務涉及到金額消費以及庫存控制,因此為了解決建立在記憶體上高併發情況下的事務控制,使用了spring封裝的RedisTemplate執行lua指令碼進行原子性操作,確保金額消費,庫存按順序處理,解決資源爭搶。  1.使用lua指令碼  Redis 使用單個 L

利用shell指令碼node.js

利用shell指令碼管理node.js啟動相關動作 利用shell指令碼管理node.js程式的啟動,停止和重啟動作 啟動node.js入口檔案 停止node.js入口檔案 重啟node.js入口檔案 快捷鍵 指令碼名 start

指令碼nginx程序

        工作中經常要殺掉nginx對應的程序,然後手動命令重啟和刪除nginx日誌,每次都要輸入3個或3個以上的命令,效率低。寫個指令碼萬事大吉。#!/bin/sh NAME="nginx

lua指令碼載入解析機制

blog的原始碼參考來自於lua5.3.4 lua位元組指令處理 指令碼指令處理過程一般分為 載入指令碼、解析指令碼、生成虛擬機器能處理的指令、根據生成的指令執行相應的功能。lua虛擬機器支援.lua指令碼檔案載入解析以及lua指令碼字串的解析處理。lua位元組

Lua用於遊戲運行期熱更(不遊戲客戶端)

服務端 道理 IT path 客戶 內存 每次 調用 清除 lua在Unity的熱更新中,一般是每次客戶端啟動的時候檢查是否有更新,然後加載。 如果要在運行期熱更,跟服務端做不停服熱更(比如java 用類構造啟動java腳本本身,或者luaJ)是一個道理。

簡單的Linux下多個tomcat服務的指令碼

在運維的過程中,我們經常會遇到一臺伺服器部署了多個Tomcat的情況。當重啟這些Tomcat的時候就會有大量的重複kill和startup的工作。所以我這裡將這些重複的工作寫成了一個指令碼。 1、指令碼如下圖所示 #!/bin/bash #獲取XXX專案程序ID tomcatpid=`p

監控Tomcat服務,自動指令碼

針對測試環境程式異常關閉,由於測試環境安裝Supervisor太費勁了,就自己寫了一個小指令碼,希望能解決大家的問題,指令碼內容如下: #!/usr/bin/python # coding=utf-8 import subprocess import datetime import time

php 用swoole 實現定時器 執行linux指令碼,檢查程序掛了,操作

利用swoole的定時器,每兩秒檢查一下 class Grep  {          const PORT = 9999;     public function port()  &n

Flink Restart Strategies策略機制深入剖析-Flink牛刀小試

版權宣告:本套技術專欄是作者(秦凱新)平時工作的總結和昇華,通過從真實商業環境抽取案例進行總結和分享,並給出商業應用的調優建議和叢集環境容量規劃等內容,請持續關注本套部落格。版權宣告:禁止轉載,歡迎學習。QQ郵箱地址:[email protected],如有任何問題,可隨時聯絡。 寫在前面的話

shell指令碼-監控python是否在執行,沒有則python

有時候需要一些python指令碼去常連結一些東西,就需要監控是否執行正常。 #!/bin/bash project1='/root/1.py' project2='/root/2.py' for Pro in $project1 $project2 do PythonPid

Linux shell指令碼啟動 停止 jar包

  最近做的微服務jar包想弄在持續整合中自動化部署,所以首先得有一個操作jar包的指令碼 只需將jar檔案的路徑替換到APP_NAME的值就可以了,其他不用改 注意:window編輯的shell檔案,通過WinSCP上傳的Linux伺服器,需要改變檔案的格式(檢視檔案格式,vim編

用Shell指令碼定時監控Linux下的程序狀態並自動

以mysql為例,先上shell指令碼,如下: #!/bin/bash ps -ef | grep mysqld | grep -v grep if [ $? -ne 0 ] then echo “start process…” /etc/rc.d/init.d

Tomcat指令碼For Windows

Tomcat 重啟指令碼,送給有需要的 JSP 環境運維同行們~ 執行環境:XP/windows 2003 測試通過,其他環境由於手頭上條件限制未測試; 指令碼功能:在常規呼叫 tomcat 自帶的關閉/重啟指令碼中加入假死判斷,若出現假死則予以強行 Kill 掉相關 JAVA 程序; 指令碼特點:可在 t

編寫springboot專案jar包工程啟動、停止、指令碼

關於springboot如果打包成jar專案,其執行方式無論是maven還是其他工具,其原理都同java -jar my.jar,呼叫jar包專案的主應用類啟動。 生產linux伺服器下,可以編寫統一指

HbaseRegionserver通過指令碼自動

環境:Hdp2.5 + hbase 1.2 + linux環境,5個數據節點 場景: 由於平臺提供出去使用,時常有一段時間進行大量資料的寫入與查詢,這時可能會導致Hbase RegionServer出現宕機的情況。為了保證對資料寫入與查詢不產生影響,分別間