§協調關閉
Play 結合使用 Pekko 的 協調關閉,但仍未完全依賴它。雖然協調關閉負責 Play 的完整關閉,但仍有 ApplicationLifecycle
API,而 Play 負責退出 JVM。
在執行階段中,觸發乾淨關閉的觸發器可能是 SIGTERM
,或者如果您的 Play 程序是 Pekko 集群的一部分,則可能是 Downing
事件。
注意:如果您使用 Play 嵌入式,或者在測試中手動處理
Application
和Server
,則在 Play 內部遷移到協調關閉可能會影響您的關閉程序,因為使用協調關閉會對Application
和Server
的依賴生命週期產生一些小的變更
1. 呼叫Server#stop
必須停止Server
,並且也必須停止在該Server
上執行的Application
2. 呼叫Application#stop
必須停止Application
,並且也可能會停止執行應用程式的Server
。
§與 ApplicationLifecycle
相較之下如何?
協調關閉提供了多個階段,您可以在這些階段中註冊任務,而 ApplicationLifecycle
只提供一種註冊停止掛勾的方式。傳統的 ApplicationLifecycle
停止掛勾是一連串的作業,Play 會決定它們執行的順序。協調關閉使用一個有向無環圖 (DAG) 中組織的階段集合,其中單一階段中的所有任務並行執行。這表示如果您將清理程式碼從 ApplicationLifecycle
移到協調關閉,您現在也必須指定程式碼應該在什麼階段執行。如果您想要精細地控制應用程式中看似無關的部分應該關閉的順序,您可以使用協調關閉取代 ApplicationLifecycle
。例如,如果您想要透過開放的 Websocket 發出應用程式即將關閉的訊號,您可以在名為 before-service-unbind
的階段上註冊一個任務,並讓該任務將訊號訊息推送到 Websocket 客戶端。before-service-unbind
被允許在 service-unbind
之前發生,而 service-unbind
是您猜對了,伺服器連線解除繫結的階段。解除連線的繫結仍會允許正在進行的請求完成。
Play 的 DI 會公開 CoordinatedShutdown
的執行個體。如果您想要從 ApplicationLifecycle
遷移到協調關閉,無論您在何處要求注入 ApplicationLifecycle
的執行個體,您現在都可以要求 CoordinatedShutdown
的執行個體。
CoordinatedShutdown
執行個體會繫結到 ActorSystem
執行個體。在 Server
和 Application
共享 ActorSystem
的那些環境中,當其中一個停止時,Server
和 Application
都會停止。您可以在 Play 手冊上的協調關閉 的新區段中找到更多詳細資訊,或者您可以查看 Pekko 的 協調關閉參考文件。
§關閉順序
協調關閉會隨著一組預設階段發布,這些階段組織成有向非循環圖 (DAG)。您可以建立新的階段並覆寫預設值,讓現有的階段依賴於您的階段。以下是 Pekko 中附帶且 Play 預設使用的最相關階段清單
before-service-unbind
service-unbind
service-requests-done
service-stop
// few cluster-related phases only meant for internal use
before-actor-system-terminate
actor-system-terminate
上述清單會依預設執行的順序提到相關階段。請遵循 Pekko 文件 來變更此行為。
請注意,您未遷移至協調式關閉任務的ApplicationLifecycle#stopHooks
仍會以建立的相反順序執行,且會在協調式關閉的service-stop
階段中執行。也就是說,協調式關閉會將所有ApplicationLifecycle#stopHooks
視為單一任務。協調式關閉讓您彈性地在不同階段執行關閉任務。您目前使用ApplicationLifecycle#stopHooks
的程式碼應該沒問題,但請考慮檢視其呼叫方式和時機。例如,如果您有一個會定期執行某些資料庫操作的 Actor,則該 Actor 需要資料庫連線。根據這兩個物件的建立方式,您的資料庫連線池可能會在service-stop
階段的ApplicationLifecycle#stopHook
中關閉,但您的 Actor 可能不會在稍後發生的actor-system-terminate
階段關閉。
如果您在service-stop
階段執行清除程式碼符合您的使用方式,請繼續使用ApplicationLifecycle#stopHooks
。
若要選擇使用協調式關閉任務,您需要注入CoordinatedShutdown
實例,並使用addTask
,如下面的範例所示
- Scala
-
class ResourceAllocatingScalaClass @Inject() (cs: CoordinatedShutdown) { // Some resource allocation happens here: A connection // pool is created, some client library is started, ... val resources = Resources.allocate() // Register a shutdown task as soon as possible. cs.addTask(CoordinatedShutdown.PhaseServiceUnbind, "free-some-resource") { () => resources.release() } // ... some more code }
- Java
-
public class ResourceAllocatingJavaClass { private final Resources resources; @Inject public ResourceAllocatingJavaClass(CoordinatedShutdown cs) { // Some resource allocation happens here: A connection // pool is created, some client library is started, ... resources = Resources.allocate(); // Register a shutdown task as soon as possible. cs.addTask( CoordinatedShutdown.PhaseServiceUnbind(), "free-some-resource", () -> resources.release()); } // ... some more code }
§關閉觸發器
Play 程序通常會透過SIGTERM
訊號終止。當 Play 程序收到訊號時,會執行 JVM 關閉掛鉤,導致伺服器透過呼叫協調式關閉而停止。
其他可能的觸發器與 SIGTERM
略有不同。雖然 SIGTERM
以由外而內的方式處理,但您可能會觸發程式碼關閉(或程式庫可能會偵測到觸發關閉的原因)。例如,在將 Play 程序當作 Pekko 集群的一部分執行或在 API 上新增端點時,這將允許管理員或協調器觸發程式關閉。在這些情況下,關閉是由內而外的:協調關閉清單的所有階段都會按適當順序執行,但 Actor 系統會在 JVM 關閉掛鉤執行前終止。
在開發 Play 應用程式時,您應該考量所有終止觸發器以及它們將執行的步驟和順序。
§限制
Pekko 協調關閉隨附一些設定,讓它非常容易設定。儘管如此,在 Play 生命週期中使用 Pekko 協調關閉會讓其中一些設定無效。其中一個這樣的設定是 pekko.coordinated-shutdown.exit-jvm
。在 Play 專案中啟用 pekko.coordinated-shutdown.exit-jvm
會在關閉時造成死結,導致您的程序無法完成。一般來說,在所有製作、開發和測試模式中調整 Pekko 協調關閉的預設值就應該沒問題。
§優雅地關閉伺服器
伺服器後端關閉會優雅地進行,而且會遵循 Pekko HTTP 文件 中所述的步驟。以下摘要適用於 Pekko HTTP 伺服器後端,但 Play 設定 Netty 伺服器後端以盡可能遵循這些步驟。
- 首先,伺服器埠會解除繫結,而且不會接受新的連線(也適用於 Netty 後端)
- 如果要求「正在進行中」(由使用者程式碼處理),則會給予完成的硬截止時間。對於 Pekko HTTP 和 Netty 後端,可以使用
play.server.terminationTimeout
設定截止時間(有關更多詳細資訊,請參閱 Pekko HTTP 設定 或 Netty 設定 中的一般伺服器設定)。 - 如果連線沒有「正在進行中」的要求,則會立即終止。
- 如果使用者程式碼在逾時內發出回應,則會將此回應傳送給客戶端,並附上
Connection: close
標頭,且會關閉連線。 - 如果是串流回應,則也強制要求必須在截止時間內完成,否則會終止連線,而與串流回應的狀態無關。
注意:Pekko HTTP 包含 一個錯誤,導致它在正常終止期間未考慮回應實體串流。作為解決方法,您可以將play.server.waitBeforeTermination
設定為所需的延遲,以讓這些回應有時間完成。有關此設定的更多詳細資訊,請參閱 Pekko HTTP 設定 或 Netty 設定 中的一般伺服器設定。 - 如果使用者程式碼未在截止時間內回覆回應,則會傳送一則自動回應,其狀態由
pekko.http.server.termination-deadline-exceeded-response
設定。此值必須是有效的 HTTP 狀態碼。
在此文件發現錯誤?此頁面的原始程式碼可以在 此處 找到。閱讀 文件指南 後,請隨時提交拉取請求。有問題或建議要分享嗎?前往 我們的社群論壇 與社群展開對話。