生命周期是一個真正需要關注的基礎。應用程序很少接觸或感知到系統的生命周期。系統組件、管理員控制臺、應用組建、actors會經過很長一段時間的初始化,并且為了traffic了解系統生命周期,系統必須在可用前完全初始化。后面包括諸如控制器(controller)、緩存加載器(cache loaders)、設備初始化器等等。
squbs運行時暴露以下生命周期狀態:
Starting - squbs啟動時的狀態
Initializing - Unicomplex已啟動. Services啟動中. Cubes啟動中. 等待初始化報告.
Active - 準備工作和執行服務調用
Failed - Cube可能沒有啟動
Stopping - Unicomplex收到GracefulStop消息 停止cube, actor, 和未綁定的service.
Stopped - 運行中的squbs已停止. Unicomplex終止. ActorSystem終止.
生命周期鉤子(Hooks)
大多數的actor不關心他們什么時候啟動或者停止。然而,他們可能為同一類actor,在他們達到接收一般trafiic狀態前需要初始化。同樣的,一些actor同樣關心在關閉前被通知到,以允許在發送他們毒藥丸(poison pill)之前適當的清理。生命周期的鉤子存在就是為了這個原因。
你可以通過發送ObtainLifecycleEvents(狀態: LifecycleState*)
至Unicomplex()
將你actor注冊到生命周期事件中。一旦系統的狀態改變,你的actor將會接收到生命周期狀態。
你同樣可以通過向Unicomplex()
發送SystemState
來獲得當前的系統狀態。你將會獲得以上其中一種狀態的回復。所有的系統狀態對象繼承自org.squbs.unicomplex.LifecycleState
, 并且所有都屬于 org.squbs.unicomplex
包,見下列表:
case object Starting extends LifecycleState
case object Initializing extends LifecycleState
case object Active extends LifecycleState
case object Failed extends LifecycleState
case object Stopping extends LifecycleState
case object Stopped extends LifecycleState
啟動鉤子(Hooks)
一個actor希望參與初始化時必須指出,于是在squbs的元數據 META-INF/squbs-meta.conf如下:
cube-name = org.squbs.bottlecube
cube-version = "0.0.2"
squbs-actors = [
{
class-name = org.squbs.bottlecube.LyricsDispatcher
name = lyrics
with-router = false # Optional, defaults to false
init-required = true # 告知squbs我們需要在發送所有已經啟動前等待該actor
}
任何設置init-required為true的actor需要發送一個已完成(報告)消息至cube管理者,即這些well known actor的父actor。這個報告是Try[Option[String]]
類型,允許actor報告初始化成功和失?。赡軘y帶異常)。一旦所有的cube成功的初始化完成,運行中的squbs轉變至Active 狀態。這同樣表示每個設置init-required為true的actor提交了初始化成功的報告。如果任何cube報告一個初始化失敗的報告,運行中的squbs會以一個Failed狀態取而代之。
關閉鉤子(Hooks)
停止Actors
特性org.squbs.lifecycle.GracefulStopHelper
讓用戶在他們自己的代碼中實現優雅的關閉actor。你可以按如下的方式在你的actor中混合(mix)這個特性(trait):
scala
class MyActor extends Actor with GracefulStopHelper {
...
}
這個特性提供了一些幫助方法來支持在squbs框架中優雅的關閉actor
StopTimeout
scala
/**
* 優雅的關閉actor的超時時間
* Override it for customized timeout and it will be registered to the reaper
* Default to 5 seconds
* @return Duration
*/
def stopTimeout: FiniteDuration =
FiniteDuration(config.getMilliseconds("default-stop-timeout"), TimeUnit.MILLISECONDS)
你可以復寫這個方法來指出優雅的關閉actor所需要執行的大概時間。一旦actor啟動,它將通過 StopTimeout(stopTimeout)
消息將stopTimeout
發送至它們的父節點。如果你關心它,你可以在父actor中處理這條信息。
如果你混合(mix)這個特性在你的actor代碼中,你應該在你的receive
方法中接收GracefulStop
消息,因為只有在這種情況下你可以勾住你的代碼來執行一個優雅的停止(你無法向PoisonPill
添加自定義行為)。管理者將僅僅傳播 GracefulStop
消息至他們那些混合了GracefulStopHelper
特性的子節點。子節點的實現應該在他們的receive
塊中處理這個消息。
我們還提供了以下兩個默認策略:
/**
* Default gracefully stop behavior for leaf level actors
* (Actors only receive the msg as input and send out a result)
* towards the `GracefulStop` message
*
* Simply stop itself
*/
protected final def defaultLeafActorStop: Unit = {
log.debug(s"Stopping self")
context stop self
}
/**
* Default gracefully stop behavior for middle level actors
* (Actors rely on the results of other actors to finish their tasks)
* towards the `GracefulStop` message
*
* Simply propagate the `GracefulStop` message to all actors
* that should be stop ahead of this actor
*
* If some actors failed to respond to the `GracefulStop` message,
* It will send `PoisonPill` again
*
* After all the actors get terminated it stops itself
*/
protected final def defaultMidActorStop(dependencies: Iterable[ActorRef],
timeout: FiniteDuration = stopTimeout / 2): Unit = {
def stopDependencies(msg: Any) = {
Future.sequence(dependencies.map(gracefulStop(_, timeout, msg)))
}
stopDependencies(GracefulStop).onComplete({
// all dependencies has been terminated successfully
// stop self
case Success(result) => log.debug(s"All dependencies was stopped. Stopping self")
if (context != null) context stop self
// some dependencies are not terminated in the timeout
// send them PoisonPill again
case Failure(e) => log.warning(s"Graceful stop failed with $e in $timeout")
stopDependencies(PoisonPill).onComplete(_ => {
// don't care at this time
if (context != null) context stop self
})
})
}
停止Squbs插件
通過復寫org.squbs.lifecycle.ExtensionLifecycle
中的shutdown()
方法,你可以添加在擴展關閉中添加個性化的行為。需要指出的是這個方法在所有安裝好的擴展中將在actor系統停止后執行。如果任何擴展在關閉的時候拋出了異常,JVM會以-1退出。