Akka Cluster Sharding

Akka Cluster Sharding

96 
DeepLearningZ 
 0.1 2018.03.22 10:57* 字數 7911 閱讀 449評論 1

  Sharding分片這個名詞從P2P時代開始早已有之,對於分佈式存儲意味如何決定數據存到哪臺節點機去、對於分佈式計算意味如何決定計算分佈到哪臺節點機上,Akka中的分片兼而有之,用於將實體(帶狀態actor,既有數據屬性也有作計算的微服務屬性)分佈到多個節點機以實現負載接近均衡的分佈式微服務,全部實體仍然屬於一個統一集羣。分佈基於一個肯定而簡單的分片規則,Akka分片規則是一個算法/純函數:輸入實體ID、輸出通常是這個實體所在數據分片的shard ID(分片和ShardRegion片區之間有映射、片區和節點機之間有映射),實體ID是實體核心要素,必須有,utf-8編碼字符串。html

  重點在於:由於它是純函數,隨時隨地能夠調用無需依賴外界:無論在什麼地方、何時調用必定獲得一個惟一的肯定結果:在實體初始分片時調用、在查找定位實體物理位置時調用,惟一須要保證的是實體ID是穩定的,無需分佈式協調便可隨時隨地知道數據應該發給誰、應該找誰要。典型分片規則算法:A simple sharding algorithm that works fine in most cases is to take the absolute value of the hashCode of the entity identifier modulo number of shards.java

  ActorSelection雖然能夠按物理路徑查詢actor但須要指定搜索哪一臺節點機:node

    val selection = context.actorSelection("akka.tcp://actorSystemName@10.0.0.1:2552/user/actorName")git

  這沒多大屁用,可是加上一個簡單的分片規則,使得actor的分佈和定位自動化,實用價值立馬爆棚,關鍵在於自動化,自動化定位使得分佈式擺脫了物理地址的束縛,實現使用邏輯名便可與actor交互,往遠了說雲也不過是運維自動化——actor位置透明+經過ShardRegion轉發,使一切變得簡單github

  Sharding具有一個分佈式分片管理層,推薦草原老師在InfoQ的演講,當時的lightBend測試博客。注意演講是2014年(Akka剛推出2.三、ShardRegion的shardID提取器叫Resolver、shard叫region),其餘參考Akka分片集羣。sharding叫作分片以區別集羣的網絡分區故障,分片上下級結構是:redis

    集羣(多臺節點機)  —>  每臺節點機(1個片區)  —>  每一個片區(多個分片)  —>  每一個分片(多個實體)算法

  ShardRegion是在集羣每臺參與分片的成員節點機上部署的負責管理分片的actor咱們叫它片區,它是一種Stub駐點服務、long Running的系統actor,您有關於集羣的問題?找它就對了,並且不限於分片、集羣問題哦,它比你瞭解的更強大:後端

  全部片區組成分佈式分片管理層,這也是一個分佈式路由層,攜帶實體ID的消息直接發給本機片區,分片管理層就能夠爲你路由,根據消息的實體ID路由到相應節點機上的實體。所以,Sharding要求你必須提供從消息抽取分片和實體ID的函數、在這裏分片ID抽取函數之因此也要提供,由於分片ID是從實體ID根據分片規則運算獲得、這個分片規則是可插拔的,由你定製由你提供。緩存

 

  Akka文檔把實體類比爲DDD聚合根:It could for example be actors representing Aggregate Roots in Domain-Driven Design terminology. Here we call these actors 「entities」. These actors typically have persistent (durable) state.網絡

  針對數據變化,把一組相關的對象劃分爲一個單元來考慮,即爲聚合。聚合使用邊界將內部和外部的對象劃分開,每一個聚合有一個根,這個根是一個實體而且,它是外部能夠訪問的惟一對象。根能夠保持對任意聚合對象的引用,聚合內的對象也能夠互相持有,但外部對象只能持有根對象的引用,顯而易見,外部想要變動聚合內的對象必須/只能經過聚合根,根擁有全局標識符,而且有責任管理不變量。Akka想告訴咱們的是actor能夠建立、持有大量其餘的actor,固然,這些對象都必須和該actor的業務定位相關,都是爲這個actor所用的;而後,這些對象不能「逸出」、不能拋頭露面。最後,因爲在Akka世界中是一切皆actor模式,因此事實上根actor持有的是一些其餘的actorRef,這就好理解了,說它是職責分派、worker替身模式均可以。

  實體必須有本身的惟一ID、分片也同樣,分片ID用於一致性哈希分片。實體相似於集羣單例Cluster Singleton,若是你只用獲得少許的分片actor,一臺機器的資源也能夠承載,那麼爲了簡單起見用Cluster Singleton就夠了,在此也能夠看出實體和單例的一個共同點是:集羣保證在一個時刻,一個實體/單例只會運行在一臺節點機上,能夠說ShardRegion是ClusterSingletonManager(都是本地Stub)與ClusterSingletonProxy(都是分佈式通信代理)的合體。

  實體actor的特色首先是徹底由分片集羣託管、其建立及生命週期不受咱們的控制;其次是不能直接給實體發消息,而是必須經過本機片區和實體所在片區轉發;最後是實體必須具有全局(整個集羣)惟一ID.

  分片集羣能夠提供相似send分佈式發佈訂閱、集羣客戶端功能,由於片區也是分片集羣的消息路由器,消息的發送者不須要知道目標實體實際位置,只要發給本機片區就好(只要攜帶目標實體的ID便可),再者,咱們可使用角色控制節點機的身份,指定哪些核心節點機參與分片,在一臺非分片角色的節點機上你也能夠建立片區ShardRegion,此時它屬於proxy only mode,它能夠向分片集羣路由消息,但不參與分片。

  建立片區的代碼就是加入分片集羣的過程,示例代碼中的實體同時繼承了PersistentActor,雖然持久化不是必須的,仍是能看出來Akka仍是側重分片集羣搭配持久化總體解決方案,兩者也確實關聯頗多,在Akka2.3一塊推出。不過Akka持久化稍顯死板,由於要在恢復時重演的Events消息、必須在收到Command消息時先行持久化,若是你不想持久化到磁盤,能夠考慮使用redis插件等分佈式緩存,但這又引入了第三方進程間通信,並且Akka本身的ddata是不適用大數據量高併發業務數據的。建立片區你必須提供兩個關鍵函數:

      1.extractEntityId:從消息當中提取實體ID的函數;

      2.extractShardId:從消息當中提取分片ID,實際上分片ID是從實體ID經過分片規則算出來的,默認實現就夠用,參見:ShardRegion.HashCodeMessageExtractor;

  在系統初始化完成後、片區接收到第一條消息時會向ShardCoordinator中央協調者詢問shard位置,協調者決策由哪個片區來持有特定shard分片,隨後該片區會建立Shard分片actor、分片再建立獨立實體,後面再來的消息再也不查詢協調者直接路由:ShardRegion —> Shard —> Entity.

  消息能夠發送給任意一臺節點機、而後最多通過一跳到達目標節點機,咱們只用Akka理論上能夠構建一個小規模、半自動的雲,負載均衡沒那麼好、通用性僅限於JVM,這是專有場景下夠用的私有云,一個properly適當的雲。

 
無論這個世界有沒有權威,在你內心不能有權威

  片區管理着一組shard(A shard is a group of entities that will be managed together.),相似HBase的RegionServer管理的region數據分區,片區就是「Shard Home」;片區還具有遠程路由能力,對集羣的其餘片區、分片和實體的分佈瞭然於胸;面對集羣你沒必要茫然,分片集羣每臺節點機都有完整一致的路由能力,你只要找你本機片區就夠了,片區SR就是你的首席集羣大管家

 
由於是本機駐點服務,片區大管家盡職盡責隨叫隨到,你能夠隨時經過ClusterSharding.shardRegion召見大管家,不能再貼心了

  對extractShardId函數的惟一硬性要求是實體ID和分片ID之間必須是一對一或者多對一關係:For a specific entity identifier the shard identifier must always be the same.  extractShardId是經過分片算法從EntityID算出來的ShardID,你能夠自定義分片算法,設計目標是:首先假設以實體做爲負載均衡最小單元、在此假設基礎之上力求全部分片具有相同數量的實體,這裏隱含了對實體的一個設計原則:

         實體儘可能能夠預期工做負載而且全部實體工做負載儘可能近似

  這就是Akka的properly、半自動的負載均衡,它不能像雲同樣根據運行時機器實際負載情況去均衡、而是依賴於你的設計確切說是你對實體的劃分是否均衡,Akka能作出的努力是基於你合理的設計能夠把實體儘可能平均分佈到全部分片上去。簡單得體的分片算法是用實體ID與最大分片數numberOfShards求餘獲得分片ID,求餘這種最簡單的運算,能夠將無限數字(實體ID)對分片數求餘以後映射到一個小於該分片數的定值整數,對無限遞增的實體ID求餘獲得的數字在{0, numberOfShards}這個區間振盪. i.e. 你始終能獲得一個合法的分片shardID,這是個小學二年級學到的規律:餘數小於除數。注意兩點:

  一、numberOfShards必須固定不變,因此寫在代碼裏也是無妨,numberOfShards和HBase的region分區數量的不一樣在於,咱們能夠自定義numberOfShards的值,只要不是太大或者過小都無妨,好比就設100,那麼從2臺到100臺的集羣都適用。

  二、實體ID最好具有隨機性:它的隨機性決定了實體分佈的均衡性,因此能夠遵循DDD將實體以中文命名,用名稱的hashCode做爲實體ID.

  最大分片數numberOfShards是你按照規劃的集羣規模所定義的你打算分多少片,最佳實踐是分片數應該是規劃的最多節點機數量的十倍大. i.e. 每臺機10個shard,好比10臺機則分100片。分片數少於節點機臺數會形成一些節點機分不到shard. 過多的分片數會形成沒必要要的分片管理負擔,e.g.  形成再平衡分片負擔、還會增長延遲,由於路由給shard第一條消息時會進行各類協做協調,這裏隱含每臺節點機超過一個數量級(10個)的分片可能會開始讓你感受到一丟丟負擔,節點機<=10個分片狀況,沒有明顯的管理負擔,大體估計每節點機10~100個分片都不是問題。

  集羣節點機隨着伸縮能夠變,可是numberOfShards不能變,分片算法在集羣全部節點也必須是一致的,分片算法的目的是Try to produce a uniform distribution 產生一個統一分佈,兩者都是不可滾動升級的,也就是說要改變numberOfShards或分片算法,必須停掉集羣全部節點機。 As a rule of thumb, the number of shards should be a factor ten greater than the planned maximum number of cluster nodes.  舉例來講:10臺機100個分片、宕掉9臺、那麼剩一臺機也得本身跑100個分片,100個分片的集羣最多擴容到100臺機,101臺就會有一臺分不到分片運行,100分片就能夠知足1~100臺機的伸縮。

  除了負載均衡,Akka的「半自動」還體如今可伸縮方面,物聯網領域咱們的集羣規模通常是比較固定的,好比就幾十臺機的中小規模集羣,那麼Akka就用這幾十臺機,而不是像雲同樣在成千上萬臺機上去伸縮。在代碼中,ShardRegion.ExtractEntityId是 「標記類型」Marker type of entity identifier (`String`). 在ShardRegion中醬紫定義的:

  type EntityId = String

  scala的類型還能夠更加富有表達力,好比extractShardId也是一個類型:

  type ExtractShardId =Msg ⇒ShardId

 
實現這個type;必須認可英語的抽象能力比漢語高得多,英語把複雜變簡單,漢語白話文言是將簡單變複雜;你的語言體現你的思想,而語言最終也會影響你的思想

How it works

for the Impatient一圖頂千言,③節點N1N2N3 ③片區 ⑨分片 加一個協調者C:

 
消息路由

  由於一個entity必然屬於一個特定shard,因此說分片Shard就是最重要的資源,SR託管Shard,SR和節點機綁定、Shard則能夠在節點機間以SR爲落腳點漂移(這就是爲何必須有SR這個駐點角色,相似的作法在其餘分佈式系統都存在:片區SR相似於hadoop裏的NodeManager、協調者C相似於ResourceManager.  因此說框架繁多,但只要諾依曼硬件架構和TCP不變、架構和通信協議就是長久不變的),Shard的漂移對SR來講即所謂的handoff換手,也就是Shard在不一樣的託管SR之間takeover交接。由此咱們看到,分佈式系統一個常規架構,就是在各個節點機上會有一個Stub駐點程序,它負責節點機自己的資源管理、與其餘駐點的通信協調,做爲服務的託管者負責服務的啓動、漂移、終止。

  分佈式系統中要確保實體單例惟一性關鍵在於全部節點具備一致的shard分佈視圖(某個shard位於哪一臺節點機,你們的見解都是一致的),因此初始的shard位置分派以及後期的調整,是由集中式的協調者C來決定,已經安排好位置的叫作resolved已知shard,片區會緩存全部resolved shard,即便你直接向一個片區發送不屬於它的消息它也會轉發給正確的片區,沒有意外狀況下,全部片區的resolved shard視圖是一致的:全部節點具備一致的shards分佈視圖。

  若是集羣有新加入成員則中央協調者就得作shards的再平衡:從一個節點遷移實體到新節點。再平衡過程當中,協調者首先通知全部片區一個 handoff換手 即將開始、全部片區會將發給該shard的消息在本地緩存、協調者對位置未定的處於再平衡階段shard的請求不予答覆直到換手完成。擁有shard的原片區會有序中止該shard下的全部實體:給他們喂handOffStopMessage(default PoisonPill) 毒丸、實體死光之後,協調者開始向新位置路由消息,全部片區緩存的消息都會路由到新位置,負責接手再平衡shard的片區會建立啓動新的實體,注意這又是一次按需建立。可是這些新實體的狀態若是須要恢復則須要你本身去使用Persistence. 綜上所述,由於分片集羣每每涉及服務遷移,而服務遷移又須要恢復服務狀態,因此分片集羣和persistence好基友是在Akka2.3一塊發佈的:For this reason Cluster Sharding and Akka Persistence are such a popular combination.

  shard的分配以及再平衡都由可插拔組件決定:ShardCoordinator.LeastShardAllocationStrategy最少分片優先分配策略,該策略會從當前擁有最多分片的片區上選取去作再平衡的分片,把它handoff轉手給當前擁有最少分片的片區,通常也就是集羣新成員,能夠配置一個閾值,該閾值指定最大達到多大的差距(最多分片和最少分片的差)就必須開始再平衡。

  各個shard的位置信息保存在中央協調者中,這些信息就至關於中央協調者自身的狀態了,爲避免單點,中央協調者的狀態默認採用Distributed Data 作容錯,當中央協調者crashed,新的協調者將會接任並恢復狀態,在此失效期間,各個片區緩存的shards保持可用、發給一些節點還不知道的shard的消息也會獲得緩存、直到新的中央協調者恢復完畢、以後就所有走正常流程。shard位置信息是典型的運行時數據,它只在系統運行起來之後產生、而且也只在系統運行期間有價值,一旦整個集羣系統都停掉了,這些信息也再也不有價值了。相比之下,實體記憶則是持久化的:The state of Remembering Entitiesis durable, i.e. it is stored to disk. The stored entities are started also after a complete cluster restart.

  只要通過同一個片區向同一個實體路由消息,則消息的順序能夠保障As long as a sender uses the same ShardRegion actor to deliver messages to an entity actor the order of the messages is preserved. 這等價於Akka的另外一個保障:兩個肯定的actor之間的消息保證送達順序,也就是消息順序得以保障的上下文是the same sender–receiver pair.  關於消息投遞可靠性保障,要作到at-least-once須要基於AtLeastOnceDelivery in Persistence.

分佈式數據 vs Persistence持久化

  節點機無論是伸仍是縮(包括有計劃地減機器和意外Crash),都會涉及到實體的遷移,那麼,實體的狀態怎麼恢復?兩種方式:ddata(Distributed Data)或persistence.

  兩者的功用同樣,沒有優劣之分,都能實現集羣容錯,協調者和片區的狀態保持默認依靠分佈式數據。若是除了作容錯你的actor沒有其餘地方用到持久化的話,爲了方便起見能夠只用分佈式數據,這樣你就不用再安裝、維護和操做第三方外部存儲了。ddata有一個閃亮亮的特性:All data entries are spread to all nodes = 高可用集羣支持n-(n-1)超級容錯

  持久化特性從一開始設計就應該固定下來,由於這個特性在整個集羣必須統一,i.e. 不可能滾動升級該特性。咱們在作實時系統時,第一直覺仍是分佈式數據更好,不過實際上持久化作存儲的時候使用的是異步actor,因此也沒太差。另外在持久化Actor中還有另外兩個概念JournalSnapshot,前者用於保存日誌流水,後者用於持久化快照,二者在Actor survive failures的時候都起到了相當重要的做用,Journal也即event stream事件流(The event stream can be queried and fed into additional processing pipelines (an external Big Data cluster for example) or alternate views(like reports). 知足這個條件的事件流持久化可用於實體的恢復,持久化還能夠用於實現消息可靠投遞、實現CQRS system.  Akka社區有HBase持久化插件也有redis的,HBase插件能夠保存journal和snapshot,基於openTSDB的底層組件asynchbase,這玩意是openTSDB公司基於本身的異步hbase操做庫開發的,不過一是咱們要去作異步持久化能夠直接基於艙壁模式去作、二是HBase官方客戶端也看到原生HBase只有同步客戶端不太好、也在慢慢加入異步feature.  最後即便HBase很快,但對併發量數據量比較高的時序業務數據仍是力不從心的。

  分佈式數據feature是默認開啓的:akka.cluster.sharding.state-store-mode = ddata/persistence則爲持久化,協調者的狀態基於它保持,是一種WriteMajority/ReadMajority多數讀寫一致性。協調者的狀態並無默認保存到磁盤,當整個集羣全部節點機宕機或停掉,狀態會永久丟失,事實上也再也不須要了。

  Remembering Entities實體記憶是持久化到磁盤的,它會持久化每一個shard所擁有的實體名單,即便是整個集羣徹底重啓,依然能夠恢復重啓以前每一個shard的實體。設置rememberEntitiesflag = true能夠開啓,該設置在調用 ClusterSharding.start時、在ClusterShardingSettings上設置。同時確保你寫的shardIdExtractor分片ID提取器具有代碼Shard.StartEntity(EntityId). 該代碼直接從一個EntityId映射到一個 ShardId,示例:

    val extractShardId: ShardRegion.ExtractShardId = {

      case EntityEnvelope(id, _)                 ⇒ (id % numberOfShards).toString

      case Get(id)                                          ⇒ (id % numberOfShards).toString

      case ShardRegion.StartEntity(id) ⇒  // StartEntity is used by remembering entities feature

            (id.toLong % numberOfShards).toString //該代碼在集羣重啓時從持久狀態恢復自動調用

}

  配置爲實體記憶後,Shard再平衡到另外一個節點上時、或者從Crash恢復時,總會從新建立以前所擁有的全部實體,這是它默認的、全自動的行爲,要徹底停掉實體,須要發送Passivate消息給實體actor的父actor,不然實體總會被從新建立,配置爲rememberEntitiesis=false的話,Shard在再平衡或Crash恢復後不會自動重建實體,實體只會在第一條屬於它的消息到達Shard時被建立一次,也就是依然是按需建立creating on demand.

  分片使用本身的分佈式複製子Replicator,在每一個節點上都有,以這種方式,你能夠指定對某些實體類型分配到某些節點上、另外的實體類型則分配到另一些節點上. Each such replicator has a name that contains the node role and therefore the role configuration must be the same on all nodes in the cluster, i.e. you can’t change the roles when performing a rolling upgrade.

  ddata配置項:akka.cluster.sharding.distributed-data.

  實體記憶的性能代價仍是有點高的,shard再平衡時,性能消耗隨着實體數量增加而增加,當前版本的AK,若是每一個shard的實體數量超過1w個的話,咱們不推薦使用該特性。

Startup after minimum number of members

  使用akka.cluster.min-nr-of-members 或 akka.cluster.role..min-nr-of-members. 能夠指定集羣開始分片的最少啓動成員節點機數量,系統直到達到該數量的片區啓動上線纔會開始shard分配,這能夠避免過多shard在啓動階段分配到第一個片區、在後續節點機陸續啓動後又再次再平衡rebalance.

Proxy Only Mode

  片區能夠是純代理模式,此時它不會建立任何實體, i.e. 只作消息的分佈式路由,這時它更像一個純路由器、屬於分佈式路由層但不參與分片、像是分片集羣的旁觀者。用ClusterSharding.startProxy專用方法能夠建立純代理Shard。好比說,作流數據處理的時候,有一些前置接收數據的節點機、還有一批後端組成分片集羣的節點機,就能夠這麼幹。再者,還能夠經過角色劃分達到一樣目的,在調用ClusterSharding.start啓動ShardRegion actor時,若是本機角色和ClusterShardingSettings指定的角色不符,則本機啓動的ShardRegion就處於純代理模式。這個feature和集羣客戶端、分佈式發佈訂閱的各自特色多是:

  一、純代理片區和發佈訂閱相比只至關於Send不能作Pub;

  二、集羣客戶端則是容許一個集羣向另外一個集羣發送消息、或者是由於某些緣由不能加入集羣的機器向集羣發送消息;

  三、分佈式發佈訂閱使用更靈活功能更豐富,容許訂閱者動態變化、支持針對主題的publish、針對path的send和SendToAll;

  綜上,若是不採用熱備方式,也就是說一個實體就只有一個,能夠採用一、2;若是採用熱備能夠考慮3,同一份數據消息發給不止一個如出一轍的功能實體。

Passivation

  鈍化,若是實體較長時間內再也不用了,能夠停掉他們減小內存佔用。還能夠定義消息接收超時receive timeout (context.setReceiveTimeout). 能夠自動鈍化。可是送達鈍化actor的消息會被刪除,要不丟消息,也就是優雅/有序鈍化,實體能夠先通知本身的父替本身代收消息、發送ShardRegion.Passivate消息給它的父也就是Shard actor.:我要鈍化掉了、歸隱磁盤,個人消息您幫我留着。那麼Shard就會緩存它的消息 between reception of Passivate and termination of the entity. 實體是實例,它鈍化實際上就是死亡,等到有它的消息來了,須要復活它來處理,算是克隆吧,但確實不是一個實例了,因此緩存消息最終會由該實體的一個新的incarnation化身也就是新實例來處理。這種用法適合移動互聯網:用戶離開/關閉app時actor鈍化掉。

監管體系

  若是你想對實體應用自定義監管策略supervisorStrategy來代替默認的重啓策略,你得寫一個全部實體的直接父actor,在裏面定義自定義策略:Escort是父、counter是子

class Escort extends Actor {

    val counter = context.actorOf(Props[Counter], "theCounter")

    override val supervisorStrategy = OneForOneStrategy() { //默認監管策略

        case _: IllegalArgumentException ⇒ SupervisorStrategy.Resume

        case _: ActorInitializationException ⇒ SupervisorStrategy.Stop

        case _: DeathPactException ⇒ SupervisorStrategy.Stop

        case _: Exception ⇒ SupervisorStrategy.Restart

    }

    def receive = { case msg ⇒ counter forward msg  }

}

  那麼,咱們可讓這個Escort負責全部實體的建立,若是實體之間還有上下級關係,那麼就在建立下級實體以後將其actorRef以消息形式發給上級實體,上級實體保持持有全部下級實體actorRef便可。這樣,只有Escort做爲全部實體的監管者,而實體間的上下級關係也具有,分離監管層次和業務上下級層次,完美。Escort就像普通實體同樣建立啓動便可:

ClusterSharding(system).start(

    typeName = "Escort",

    entityProps = Props[Escort],

    settings = ClusterShardingSettings(system),

    extractEntityId = extractEntityId,  extractShardId = extractShardId

)

注意中止的實體,當有它的消息來到時會自動從新啓動——克隆新化身。

優雅Shutdown

  給片區發送GracefulShutdown消息來優雅地手動中止它,它的shard將會遷移,期間屬於它的消息緩存和再平衡過程同樣。

  警告:不要把Cluster Sharding和Automatic Downing聯合使用。自動下線配置容許集羣分裂爲兩個獨立小集羣,這會致使多個分片和實體同時運行,這會破壞Akka集羣元數據,可能致使集羣沒法重啓。若是還用了persistence那麼還可能會破壞你的業務數據。

若是已經形成Akka集羣元數據破壞致使集羣沒法重啓,使用以下Main命令執行程序清除損壞的Akka集羣元數據:

java  -classpath  jarFiles

    akka.cluster.sharding.RemoveInternalClusterShardingData

    -2.3 entityType1 entityType2 entityType3

  該程序包含在akka-cluster-sharding jar包文件,最好使用一致的classpath 和配置,可使用sbt或Maven.  entity type實體類型和ClusterSharding.start中的一致。

  -2.3指定了刪除Cluster Sharding in Akka 2.3.x存儲的數據,由於這個版本使用了不一樣的persistenceId.

全部配置:Configuration

  想到一個actor模型的缺陷,那就是彷佛作不到精確的定時任務,由於和actor對話的方式只有一種那就是發消息給它,可是消息必須先進消息隊列,進隊列就可能會延遲,作不到在一個精確時間點上作某一件事,好比保存一個時刻的大量actor的快照斷面,延伸想到actor是否能夠容許多個郵箱,按照優先級分類好比加急郵箱。可貴挑出actor模型的毛病,它幾乎就是OO的理想國:面向對象系統是由對象及其相互之間的消息構成。

  不那麼精確的、可用於大批量數據的定時任務有個內置支持scheduler,使用步驟:

  一、在一個actor中能夠用:context.system.scheduler來獲得一個定時器,同時須要一個隱式傳遞的ExecutionContext,咱們知道它基本就是一個線程池,scheduler須要它提供的線程來執行定時任務,通常狀況下只要import context.dispatcher也就是直接使用當前的dispatcher.

  二、scheduler.scheduleOnce(time, actorRef, message)方法能夠把message消息調度給一個future 、以time爲定時、消息會被髮送一次、給actorRef也能夠是self.  因此說即便是本身的事也得用消息通知,相似記事帖,要是到了時間本身很忙呢,就可能滯後。scheduleOnce方法返回值是一個Cancellable,若是一次性的任務按時完成了,能夠用它取消timer.

  其它參考:替身模式、任務actor代碼示例

相關文章
相關標籤/搜索