檢視原始碼 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 自動轉換為低階指令。

為了避免重複升級指令,允許使用正規表示式來指定 UpFromVsnDownToVsn。若要將版本識別符視為正規表示式,必須將其指定為二進位。例如,以下符合所有版本 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_servergen_fsmgen_statemgen_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。它會指定程式碼是否為「動態」,也就是說,使用模組的程序是否會自發切換至新程式碼,或者是否為「靜態」。在執行進階更新和升級時,會先載入動態模組的新版本,再要求程序變更程式碼。降級時,會先要求程序變更程式碼,再載入新版本。對於靜態模組,無論升級還是降級,都會先載入新版本,再要求程序變更程式碼。回呼模組是動態的。

變更監督程式的啟動規格時,會使用帶有引數 supervisorupdate

{load_module, Mod}
{load_module, Mod, DepMods}
{load_module, Mod, PrePurge, PostPurge, DepMods}
  Mod = atom()
  PrePurge = PostPurge = soft_purge | brutal_purge
  DepMods = [Mod]

模組 Mod 的簡單程式碼取代。

如需 PrePurgePostPurge 的描述,請參閱上方的 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()

使用低階指令 removepurge 刪除模組 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_applicationadd_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 檔案中的最後一個指令。

另請參閱

release_handlerrelup(4)supervisorsystools