檢視原始碼 appup
應用程式升級檔案
描述
應用程式升級檔案 定義了如何在執行中的系統中升級或降級應用程式。
當產生發行升級檔案 relup
時,systools
中的函式會使用此檔案。
檔案語法
應用程式升級檔案應命名為 Application.appup
,其中 Application
是應用程式的名稱。該檔案應位於應用程式的 ebin
目錄中。
.appup
檔案包含一個單一的 Erlang 術語,它定義了用於升級或降級應用程式的指令。該檔案具有以下語法
{Vsn,
[{UpFromVsn, Instructions}, ...],
[{DownToVsn, Instructions}, ...]}.
Vsn = string()
- 目前的應用程式版本。UpFromVsn = string() | binary()
- 要從其升級的較早應用程式版本。如果它是字串,則會將其解釋為特定的版本號碼。如果它是二進位,則會將其解釋為可以匹配多個版本號碼的正規表示式。DownToVsn = string() | binary()
- 要降級到的較早應用程式版本。如果它是字串,則會將其解釋為特定的版本號碼。如果它是二進位,則會將其解釋為可以匹配多個版本號碼的正規表示式。Instructions
- 發行升級指令 的列表,請參閱 發行升級指令。建議僅使用高階指令。當建立relup
檔案時,這些指令會由systools
自動轉換為低階指令。
為了避免重複升級指令,允許使用正規表示式來指定 UpFromVsn
和 DownToVsn
。若要將版本識別符視為正規表示式,必須將其指定為二進位。例如,以下符合所有版本 2.1.x
,其中 x
是任何數字
<<"2\\.1\\.[0-9]+">>
請注意,正規表示式必須符合完整的版本字串,因此此範例適用於例如 2.1.1
,但不適用於 2.1.1.1
。
發行升級指令
當進行升級或降級時,發行處理常式會解譯發行升級指令。如需發行處理的詳細資訊,請參閱系統文件中的 OTP 設計原則。
如果 Mod
列在用於啟動程序的子程序規格的 Modules
部分中,則稱該程序使用模組 Mod
,請參閱 supervisor
。在 gen_event
的情況下,如果 Mod
是已安裝的事件處理常式,則稱事件管理員程序使用 Mod
。
高階指令
{update, Mod}
{update, Mod, supervisor}
{update, Mod, Change}
{update, Mod, DepMods}
{update, Mod, Change, DepMods}
{update, Mod, Change, PrePurge, PostPurge, DepMods}
{update, Mod, Timeout, Change, PrePurge, PostPurge, DepMods}
{update, Mod, ModType, Timeout, Change, PrePurge, PostPurge, DepMods}
Mod = atom()
ModType = static | dynamic
Timeout = int()>0 | default | infinity
Change = soft | {advanced,Extra}
Extra = term()
PrePurge = PostPurge = soft_purge | brutal_purge
DepMods = [Mod]
同步程式碼取代使用模組 Mod
的程序。
所有這些程序都使用 sys:suspend
暫停,載入新的模組版本,然後使用 sys:resume
恢復程序。
Change
- 預設為soft
,並定義程式碼變更的類型。如果設定為{advanced,Extra}
,則使用gen_server
、gen_fsm
、gen_statem
或gen_event
實作的程序會透過呼叫回呼函式code_change
來轉換其內部狀態。特殊程序會呼叫回呼函式system_code_change/4
。在這兩種情況下,術語Extra
都會作為引數傳遞至回呼函式。PrePurge
- 預設為brutal_purge
。它會控制在載入新的模組版本之前,對執行舊程式碼的程序採取的動作。如果值為brutal_purge
,則會終止這些程序。如果值為soft_purge
,則release_handler:install_release/1
會傳回{error,{old_processes,Mod}}
。PostPurge
- 預設為brutal_purge
。它會控制在載入新的模組版本後,對執行舊程式碼的程序採取的動作。如果值為brutal_purge
,則會在發行永久化時清除程式碼,並終止這些程序。如果值為soft_purge
,則當沒有剩餘程序執行程式碼時,發行處理常式會清除舊程式碼。DepMods
- 預設為[]
,並定義Mod
所依賴的其他模組。在relup
檔案中,升級時暫停使用Mod
的程序的指令會先於暫停使用DepMods
中模組的程序的指令,反之,在降級時亦然。在循環依賴的情況下,會保留appup
檔案中指令的順序。Timeout
- 定義暫停程序時的逾時。如果未指定值或指定default
,則會使用sys:suspend
的預設值。ModType
- 預設為dynamic
。它會指定程式碼是否為「動態」,也就是說,使用模組的程序是否會自發切換至新程式碼,或者是否為「靜態」。在執行進階更新和升級時,會先載入動態模組的新版本,再要求程序變更程式碼。降級時,會先要求程序變更程式碼,再載入新版本。對於靜態模組,無論升級還是降級,都會先載入新版本,再要求程序變更程式碼。回呼模組是動態的。
變更監督程式的啟動規格時,會使用帶有引數 supervisor
的 update
。
{load_module, Mod}
{load_module, Mod, DepMods}
{load_module, Mod, PrePurge, PostPurge, DepMods}
Mod = atom()
PrePurge = PostPurge = soft_purge | brutal_purge
DepMods = [Mod]
模組 Mod
的簡單程式碼取代。
如需 PrePurge
和 PostPurge
的描述,請參閱上方的 update
。
DepMods
預設為 []
,並定義 Mod
所依賴的其他模組。在 relup
檔案中,升級時,載入這些模組的指令會先於載入 Mod
的指令,反之,在降級時亦然。
{add_module, Mod}
{add_module, Mod, DepMods}
Mod = atom()
DepMods = [Mod]
載入新的模組 Mod
。
DepMods
預設為 []
,並定義 Mod
所依賴的其他模組。在 relup
檔案中,升級時,與這些模組相關的指令會先於載入 Mod
的指令,反之,在降級時亦然。
{delete_module, Mod}
{delete_module, Mod, DepMods}
Mod = atom()
使用低階指令 remove
和 purge
刪除模組 Mod
。
DepMods
預設為 []
,並定義 Mod
所依賴的其他模組。在 relup
檔案中,升級時,與這些模組相關的指令會先於移除 Mod
的指令,反之,在降級時亦然。
{add_application, Application}
{add_application, Application, Type}
Application = atom()
Type = permanent | transient | temporary | load | none
新增應用程式表示使用 add_module
載入 .app
檔案中由 modules
鍵定義的模組。
Type
預設為 permanent
,並指定應用程式的啟動類型。如果 Type = permanent | transient | temporary
,則會以對應的方式載入和啟動應用程式,請參閱 application
。如果 Type = load
,則僅載入應用程式。如果 Type = none
,則不會載入和啟動應用程式,但會載入其模組的程式碼。
{remove_application, Application}
Application = atom()
移除應用程式表示會停止應用程式、使用 delete_module
卸載模組,然後從應用程式控制器卸載應用程式規格。
{restart_application, Application}
Application = atom()
重新啟動應用程式表示會停止應用程式,然後再次啟動應用程式,類似於依序使用指令 remove_application
和 add_application
。請注意,即使應用程式已在執行發行升級之前啟動,restart_application
也可能只會 load
它,而不是 start
它,具體取決於應用程式的 start type
:如果 Type = load
,則僅載入應用程式。如果 Type = none
,則不會載入和啟動應用程式,但會載入其模組的程式碼。
低階指令
{load_object_code, {App, Vsn, [Mod]}}
App = Mod = atom()
Vsn = string()
從目錄 App-Vsn/ebin
以二進位讀取每個 Mod
。它不會載入模組。指令應放在腳本中的最前面,以便從檔案讀取所有新程式碼,以減少暫停、載入和恢復循環所花費的時間。
point_of_no_return
如果在此指令之後發生當機,系統將無法恢復,並會從舊的發行版本重新啟動。指令在腳本中只能出現一次。它應放置在所有 load_object_code
指令之後。
{load, {Mod, PrePurge, PostPurge}}
Mod = atom()
PrePurge = PostPurge = soft_purge | brutal_purge
在發生此指令之前,必須已使用 load_object_code
載入 Mod
。此指令會載入模組。PrePurge
會被忽略。如需 PostPurge
的描述,請參閱先前的高階指令 update
。
{remove, {Mod, PrePurge, PostPurge}}
Mod = atom()
PrePurge = PostPurge = soft_purge | brutal_purge
將 Mod
的目前版本設為舊版本。PrePurge
會被忽略。如需 PostPurge
的描述,請參閱先前的高階指令 update
。
{purge, [Mod]}
Mod = atom()
清除每個模組 Mod
,也就是移除舊程式碼。請注意,任何執行已清除程式碼的程序都會被終止。
{suspend, [Mod | {Mod, Timeout}]}
Mod = atom()
Timeout = int()>0 | default | infinity
嘗試暫停所有使用模組 Mod
的程序。如果程序沒有回應,則會忽略它。這可能會導致程序死亡,可能是因為它在自發切換至新程式碼時當機,或是由於清除操作而導致。如果未指定 Timeout
或指定 default
,則會使用 sys:suspend
的預設值。
{resume, [Mod]}
Mod = atom()
恢復所有使用模組 Mod
的已暫停程序。
{code_change, [{Mod, Extra}]}
{code_change, Mode, [{Mod, Extra}]}
Mod = atom()
Mode = up | down
Extra = term()
Mode
預設為 up
,並指定它是升級還是降級。此指令會透過呼叫函式 sys:change_code
,將 code_change
系統訊息傳送至所有使用模組 Mod
的程序,並將術語 Extra
作為引數傳遞。
{stop, [Mod]}
Mod = atom()
透過呼叫 supervisor:terminate_child/2
,停止所有使用模組 Mod
的程序。當變更程式碼最簡單的方式是停止並重新啟動執行程式碼的程序時,此指令很有用。
{start, [Mod]}
Mod = atom()
透過呼叫 supervisor:restart_child/2
,啟動所有使用模組 Mod
的已停止程序。
{sync_nodes, Id, [Node]}
{sync_nodes, Id, {M, F, A}}
Id = term()
Node = node()
M = F = atom()
A = [term()]
apply(M, F, A)
必須傳回節點列表。
此指令會將發行版本的安裝與其他節點同步。每個 Node
都必須以相同的 Id
評估此命令。本地節點會等待所有其他節點評估完指令後,才會繼續執行。如果節點關閉,則會被視為不可恢復的錯誤,本地節點會從舊的發行版本重新啟動。此指令沒有逾時設定,這表示它可能會永遠掛起。
{apply, {M, F, A}}
M = F = atom()
A = [term()]
評估 apply(M, F, A)
。
如果指令出現在指令 point_of_no_return
之前,則會捕獲失敗。release_handler:install_release/1
接著會回傳 {error,{'EXIT',Reason}}
,除非擲回或回傳 {error,Error}
。然後它會回傳 {error,Error}
。
如果指令出現在指令 point_of_no_return
之後,且函式呼叫失敗,系統會重新啟動。
restart_new_emulator
當應用程式 ERTS、Kernel、STDLIB 或 SASL 升級時,會使用此指令。它會關閉目前的模擬器並啟動新的模擬器。所有程序都會正常終止,並且當模擬器重新啟動時,會使用新版本的 ERTS、Kernel、STDLIB 和 SASL。relup
檔案中只允許一個 restart_new_emulator
指令,且它必須放在最前面。systools:make_relup/3,4
會在產生 relup
檔案時確保這一點。relup
檔案中的其餘指令會在重新啟動後,作為啟動腳本的一部分執行。
升級完成時會寫入資訊報告。若要以程式方式判斷升級是否完成,請呼叫 release_handler:which_releases/0,1
並檢查預期的發行版本是否具有狀態 current
。
升級完成後,仍然必須將新的發行版本設為永久,否則如果模擬器重新啟動,則會啟動舊的模擬器。
警告
如先前所述,指令
restart_new_emulator
會導致模擬器重新啟動,並使用新版本的 ERTS、Kernel、STDLIB 和 SASL。但是,所有其他應用程式在啟動時,都會在這個新的模擬器中執行舊版本。這通常沒有問題,但是核心應用程式有時會發生不相容的變更,這可能會在此設定中造成問題。此類不相容的變更 (當函式被移除時) 通常會先經過兩個主要版本的棄用。為確保您的應用程式不會因不相容的變更而崩潰,請盡快移除對已棄用函式的任何呼叫。
restart_emulator
此指令與 restart_new_emulator
類似,但必須放在 relup
檔案的結尾。它與模擬器或核心應用程式的升級無關,但任何應用程式在需要完整重新啟動系統時都可以使用。
在產生 relup
檔案時,systools:make_relup/3,4
會確保只有一個 restart_emulator
指令,且它是 relup
檔案中的最後一個指令。