作者
Maria Scott <maria-12648430(at)hnc-agency(dot)org> , Jan Uhlig <juhlig(at)hnc-agency(dot)org>
狀態
最終版本/24.0 已在 OTP 版本 24 中實作
類型
標準追蹤
建立日期
2021-03-04
Erlang 版本
OTP-24.0
發布歷史
2021-03-08、2021-03-17、2021-03-23、2021-03-31、https://github.com/erlang/otp/pull/4521

EEP 56: 由重要子程序終止觸發的自動監督者關閉 #

摘要 #

此 EEP 引入了一種基於特定標記的重要子程序終止來自動終止監督者的方法。

本文檔基於 OTP-PR 4521 中的討論。

動機 #

監督者下的子程序通常代表一個工作單元,這意味著一組協作的進程,而不僅僅是一個單一的進程。這樣的工作單元監督者(在本文檔的上下文中稱為群組監督者)通常由 simple_one_for_one 監督者託管,並根據需要通過該監督者啟動。

然而,在撰寫本文時,一旦它們代表的工作單元完成工作且各自的子程序終止,就沒有一種好的、標準的方法來停止這樣的群組監督者,這意味著群組監督者將會閒置,除非以某種方式手動停止。

這個問題已在應用程式中以多種方式解決,但沒有一種可以稱為真正好、直接或標準的方法

  • 將群組監督者的 Pid 傳遞給負責關閉該監督者的子程序。然後,通過向群組監督者發送退出訊號來實現關閉。雖然文檔中說明了適當的退出訊號,但不是用於此目的,而且隨意發送退出訊號可能很危險。
  • 將群組監督者的 Pid 和位於其上層的監督者的 Pid 傳遞給負責關閉該監督者的子程序。然後,通過告知上層監督者通過 supervisor:terminate_child/2 終止群組監督者來實現關閉。由於這是一個阻塞呼叫,因此必須產生一個進程來執行它。此外,這將導致上層監督者被阻塞,直到群組監督者關閉,因此在那之前它將不接受其他請求。

以上兩種方法都存在一個問題,即負責關閉的子程序必須知道有關其周圍環境的事情,即...

  • ...它首先位於(群組)監督者之下。在以上第二種方法中,甚至這個群組監督者又位於另一個監督者之下。相反,不可能獨立運行此進程(例如,用於測試),也就是說,沒有上層的監督者。
  • ...它可能有同級子程序,並且通過關閉監督者來導致它們關閉是安全的。從程式設計師的角度來看,僅僅通過查看監督者的實作並不明顯地看出某個子程序可能會導致監督者和所有子程序的關閉,因此存在意外的可能性。

可以通過擁有一個專用的監視子程序來解決這個問題,該子程序監視其他子程序並根據它們的行為採取行動。但是,這需要相當多的樣板程式碼來完成本應更適合在監督者中完成的任務。此外,還存在監視進程必須保持它所監視的子程序列表最新,以防其中任何一個被重新啟動的問題,可以通過啟用子程序在啟動時向其註冊(為此,它們必須知道監視進程的 pid),或向監督者請求。

另一種常用的方法是使負責關閉群組監督者的子程序永久存在,並將監督者的重新啟動強度設為 0。這樣做的缺點是,如果子程序異常退出但可以重新啟動,則子程序將不會重新啟動,而是導致監督者關閉。這種方法的另一個缺點是,即使關閉是預期的,它也會產生錯誤訊息(崩潰報告)。

最後但並非最不重要的一點是,有些人採取了克隆 OTP 監督者並根據自己的需求自訂它的方法,原因在此處和其他地方概述。

基本原理 #

此 EEP 通過引入一種方法來通過新的子程序規格標誌將特定子程序標記為 significant,以及一種配置監督者根據重要子程序的退出自動關閉的方法(通過新的監督者標誌),從而緩解了動機中概述的問題。

為了保持向後相容性,新標誌將僅在子程序規格和監督者標誌的 map 形式中使用,並且出於同樣的原因,新標誌的預設值被選擇為,在它們不存在的情況下,監督者的行為與迄今為止相同。

新的子程序規格標誌名為 significant,可能的值為 truefalse,其中 false 為預設值。

新的監督者標誌名為 auto_shutdown,可能的值為 neverany_significantall_significant,其中 never 為預設值。

如果監督者 auto_shutdown 標誌設定為 never,則不允許將子程序規格標誌 significant 設定為 truenever 值和對 significant 值的限制旨在作為一種安全手段,以防止意外的自動關閉,例如,由於通過 supervisor:start_child/2 後來添加的重要子程序的退出。由於此類子程序的規格不會出現在 supervisor:init/1 回呼程式碼中,而是在其他地方,因此調試此類無法解釋的監督者關閉可能會很困難。

否則,當一個重要子程序自行退出時,適用以下規則

  • 如果 transient 子程序異常退出,則將重新啟動(不會導致監督者關閉)。如果它正常退出...

    • 如果監督者 auto_shutdown 標誌為 any_significant,則監督者將關閉
    • 如果監督者 auto_shutdown 標誌為 all_significant,則如果子程序是最後一個活動的重要子程序,則監督者將關閉
  • temporary 子程序永遠不會重新啟動。如果它正常或異常退出,則適用與 transient 子程序相同的規則,關於監督者 auto_shutdown 標誌。

如果重新啟動類型為 permanent,則不允許將 significant 標誌設定為 true,因為這種組合沒有意義。

需要明確的是,以上規則僅在重要子程序自行退出時適用,即當通過 supervisor:terminate_child/2 手動終止時,當其他非重要子程序退出時,以及當由於 one_for_allrest_for_one 策略中同級子程序的死亡而被終止時,均不適用。

此處提出的方法也可以通過將所有子程序標記為 significant 並將監督者 auto_shutdown 標誌設定為 all_significant,來實現“當為空時關閉”的效果。

值得一提的是,simple_one_for_one 策略構成了一個特殊情況,因為它只能有一個適用於所有子程序的子程序規格。這意味著,要么所有子程序都是重要的子程序,要么沒有是。

注意事項 #

one_for_allrest_for_one 監督者中使用臨時的重要子程序可能會導致邊緣情況,其中預期的自動關閉不會發生。臨時子程序不會重新啟動,即使它們的終止是由於同級子程序的死亡造成的。另一方面,當重要子程序由於同級子程序的死亡而被終止時,不會觸發監督者的自動關閉。因此,如果預期自動關閉其監督者的臨時重要子程序由於同級子程序的死亡而被終止,則將丟失。

向後相容性 #

本文檔中提出的變更沒有引入不相容的變更,因為新的子程序規格和監督者標誌是可選的,並且預設為導致目前行為的值。此外,動機中概述的所有目前解決方法仍然有效。

儘管提出的變更是向後相容的,但是使用此增強功能的應用程式在用以前的 OTP 版本編譯時可能不相容,除非採取適當的措施。使用較舊的 OTP 版本編譯的此類應用程式將洩漏進程,因為它依靠自動監督者關閉來刪除其監視樹中未使用的部分,這將不會發生。如果實作者期望使用重要子程序行為的應用程式使用早於其出現的 OTP 版本編譯,則由他們自行決定是否解決此問題。

實作 #

可以在 OTP-PR 4638 中找到一個將更新以反映本文檔狀態的參考實作。

版權 #

本文檔已置於公共領域。