文件

§協調關閉

Play 結合使用 Pekko 的 協調關閉,但仍未完全依賴它。雖然協調關閉負責 Play 的完整關閉,但仍有 ApplicationLifecycle API,而 Play 負責退出 JVM。

在執行階段中,觸發乾淨關閉的觸發器可能是 SIGTERM,或者如果您的 Play 程序是 Pekko 集群的一部分,則可能是 Downing 事件。

注意:如果您使用 Play 嵌入式,或者在測試中手動處理 ApplicationServer,則在 Play 內部遷移到協調關閉可能會影響您的關閉程序,因為使用協調關閉會對 ApplicationServer 的依賴生命週期產生一些小的變更
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 執行個體。在 ServerApplication 共享 ActorSystem 的那些環境中,當其中一個停止時,ServerApplication 都會停止。您可以在 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 伺服器後端以盡可能遵循這些步驟。

  1. 首先,伺服器埠會解除繫結,而且不會接受新的連線(也適用於 Netty 後端)
  2. 如果要求「正在進行中」(由使用者程式碼處理),則會給予完成的硬截止時間。對於 Pekko HTTP 和 Netty 後端,可以使用 play.server.terminationTimeout 設定截止時間(有關更多詳細資訊,請參閱 Pekko HTTP 設定Netty 設定 中的一般伺服器設定)。
  3. 如果連線沒有「正在進行中」的要求,則會立即終止。
  4. 如果使用者程式碼在逾時內發出回應,則會將此回應傳送給客戶端,並附上 Connection: close 標頭,且會關閉連線。
  5. 如果是串流回應,則也強制要求必須在截止時間內完成,否則會終止連線,而與串流回應的狀態無關。
    注意:Pekko HTTP 包含 一個錯誤,導致它在正常終止期間未考慮回應實體串流。作為解決方法,您可以將 play.server.waitBeforeTermination 設定為所需的延遲,以讓這些回應有時間完成。有關此設定的更多詳細資訊,請參閱 Pekko HTTP 設定Netty 設定 中的一般伺服器設定。
  6. 如果使用者程式碼未在截止時間內回覆回應,則會傳送一則自動回應,其狀態由 pekko.http.server.termination-deadline-exceeded-response 設定。此值必須是有效的 HTTP 狀態碼

下一步:與 Pekko 型別和叢集分片整合


在此文件發現錯誤?此頁面的原始程式碼可以在 此處 找到。閱讀 文件指南 後,請隨時提交拉取請求。有問題或建議要分享嗎?前往 我們的社群論壇 與社群展開對話。