當處理到來的消息流時,我們需要一個actor來引導(dǎo)消息路由到目標actor,從而提高消息的分配效率。在Akka中這個 actor就是Router。它所管理的一些目標actor叫做routees
根據(jù)不同的情況需要,Akka提供了幾種路由策略。當然也可以創(chuàng)建自己的路由及策略。
一個簡單的Router
如何使用Router 還有如何管理routees。

Akka提供的路由策略如下:(詳細見下文路由的使用)
akka.routing.RoundRobinRoutingLogic? ?輪詢
akka.routing.RandomRoutingLogic? ? 隨機
akka.routing.SmallestMailboxRoutingLogic? ?空閑
akka.routing.BroadcastRoutingLogic? ?廣播
akka.routing.ScatterGatherFirstCompletedRoutingLogic? ?分散聚集?
akka.routing.TailChoppingRoutingLogic? ? ??尾部斷續(xù)?
akka.routing.ConsistentHashingRoutingLogic? ? 一致性哈希
創(chuàng)建Router Actor
創(chuàng)建router actor 有兩種方式:
? ? ? ? ? ? 1. Pool(池)——routees都是router?的子actor,如果routees終止,router將把它們移除
? ? ? ? ? ? 2.Group(群組)——routees都創(chuàng)建在router的外部,router通過使用actor來選擇將消息發(fā)送到指定路徑,但不監(jiān)管routees是否終止。
Router actor 向 routees 發(fā)送消息,與向普通actor發(fā)送消息一樣通過其ActorRef。Router actor 不會改變消息的發(fā)送人,routees 回復(fù)消息時發(fā)送回原始發(fā)件人,而不是Router actor。
Pool(池)
可以通過配置并使用代碼在配置中獲取的方法來實現(xiàn)? (例如創(chuàng)建一個輪詢Router向5個routees發(fā)送消息)

也可以只使用編程的方式來實現(xiàn)

遠程部署Router
既可以創(chuàng)建本地actor來作為Router,也可以命令Router在任一遠程主機上部署子actor。
需要將路由配置放在RemoteRouterConfig下,在遠程部署的路徑類中要添加akka-remote模塊

發(fā)送者
默認情況下,當一個routee發(fā)送消息,它隱式地設(shè)置自己為發(fā)送者。
sender() ! x // replies will go to this actor
但是通常將routee的父Router作為發(fā)送者更有用處。這樣可以將routees的信息隱藏起來。
sender().tell("reply", context.parent) // replies will go back to parent
sender().!("reply")(context.parent) // alternative syntax (beware of the parens!)
監(jiān)管
在Pool中,routees是Router 的子actor,所以Router也負責監(jiān)管。
可以使用Pool 的supervisorStrategy屬性來配置監(jiān)管策略。如果沒有提供配置,默認策略是“always escalate”(總是上升)。就是錯誤都會上傳到Router的監(jiān)管者進行處理。但是Router的監(jiān)管者會認為是Router本身發(fā)生錯誤,因此會重啟Router,所有的routees也將被重啟,這樣導(dǎo)致監(jiān)管的效率變低。所以我們要指定監(jiān)管策略。
Group(群組)
有時我們需要單獨的創(chuàng)建routees,然后提供一個Router來供其使用??梢酝ㄟ^將routees的路徑傳遞給Router的配置,消息將通過ActorSelection來發(fā)送到這些路徑。
同樣Group像Pool一樣有兩種方法創(chuàng)建。(為三個routee actor創(chuàng)建Router)

編程方式實現(xiàn):

路由的使用
(具體配置見Routing)
1.RoundRobinPool 和 RoundRobinGroup
Router對routees使用輪詢機制
2.RandomPool 和 RandomGroup
Router隨機選擇routees發(fā)送消息
3.BalancingPool
嘗試從繁忙的routee重新分配任務(wù)到空閑routee,所有的routee共享一個mailbox
4.SmallestMailboxPool
Router創(chuàng)建的所有routees中誰郵箱中的消息最少發(fā)給誰
5.BroadcastPool 和 BroadcastGroup
廣播的路由器將接收到的消息轉(zhuǎn)發(fā)到它所有的routee。
6.ScatterGatherFirstCompletedPool 和?ScatterGatherFirstCompletedGroup
將消息發(fā)送給所有的routees,然后等待到收到第一個回復(fù),將結(jié)果發(fā)送回原始發(fā)送者。其他的回復(fù)將被丟棄
7.TailChoppingPool 和?TailChoppingGroup
將首先發(fā)送消息到一個隨機挑取的routee,短暫的延遲后發(fā)給第二個routee(從剩余的routee中隨機挑選),以此類推。它等待第一個答復(fù),并將它轉(zhuǎn)回給原始發(fā)送者。其他答復(fù)將被丟棄。
此Router的目標是通過查詢到多個routee來減少延遲,假設(shè)其他的actor可能比第一個actor更快響應(yīng)。
8.ConsistentHashingPool 和 ConsistentHashingGroup
對消息使用一致性哈希(consistent hashing)選擇routee
有三種方式定義哪些數(shù)據(jù)作為一致性哈希鍵
·? ? 你可以定義路由的hashMapping,將傳入的消息映射到它們一致哈希鍵。這使決策對發(fā)送者透明。
·? ? 這些消息可能會實現(xiàn)akka.routing.ConsistentHashingRouter.ConsistentHashable。鍵是消息的一部分,并很方便地與消息定義一起定義。
·? ? 消息可以被包裝在一個akka.routing.ConsistentHashingRouter.ConsistentHashableEnvelope中,來定義哪些數(shù)據(jù)可以用來做一致性哈希。發(fā)送者知道要使用的鍵。
特殊處理的消息
Broadcast消息
用于向Router所有的routee發(fā)送一條消息,不管該Router通常是如何路由消息的。
PoisonPill消息
無論哪個actor收到PosionPill消息都會被停止。但是對于PoisonPill消息Router不會將其傳給routees。但仍然能影響到routees,因為Router停止時它的子actor也會停止,就可能會造成消息未處理。因此我們可以將PoisonPill包裝到Broadcast消息中。這樣Router所管理的所有routees將會處理完消息后再處理PoisonPill并停止。
Kill消息
當Kill消息被發(fā)送到Router,Router將內(nèi)部處理該消息,并且不會將它發(fā)送到其routee。Router將拋出ActorKilledException并失敗,然后Router根據(jù)監(jiān)管的策略,被恢復(fù)、重啟或終止。
Router的子routee也將被暫停,也受Router監(jiān)管的影響,但是獨立在Router外部創(chuàng)建的routee將不會被影響。
Managagement消息
發(fā)送akka.routing.GetRoutees到一個Router actor,使其回送一個包含當前使用routee的akka.routing.Routees消息。
發(fā)送akka.routing.AddRoutee到一個Router?actor會將那個routee添加到其routee集合中。
發(fā)送akka.routing.RemoveRoutee到一個Router?actor將從其routee集合刪除該routee。
發(fā)送akka.routing.AdjustPoolSize到一個Pool (Router?actor)將從其routee集合中添加或刪除該數(shù)目的routee。
動態(tài)改變大小的池
大多數(shù)Pool的routees數(shù)量是固定的,但是可以制定調(diào)整策略來動態(tài)改變routees的數(shù)量
兩種調(diào)整方式(resizer):?默認的Resizer和OptimalSizeExploringResizer
Resizer:是根據(jù)壓力的大小來調(diào)整的,繁忙的routees 占總數(shù)的百分比高于或低于某個閾值就會進行調(diào)整
OptimalSizeExploringResizer?:將池的大小調(diào)整為最佳大小,來提供最多的消息吞吐量。
它跟蹤每個Pool 的消息吞吐量來定期執(zhí)行以下三個調(diào)整操作。
? ??·? ??如果在一段時間內(nèi)沒有看到所有routees被充分利用的,就縮小規(guī)模。
? ? ·? ??隨機探索附近的池大小,以嘗試收集吞吐量指標。
? ? ·????對附近的池大小進行優(yōu)化(比其他任何附近池的)吞吐量指標更好。
Akka中的路由是如何設(shè)計的
從表面看,Router就像普通的actor,但是它們實際實現(xiàn)是不同的。路由器在收消息和快速發(fā)消息給routee被設(shè)計的極度優(yōu)化。
Router可以通過優(yōu)化原有消息處理pipeline來支持多線程,從而達到更高的吞吐量。通過將路由策略直接嵌入到其ActorRef ,發(fā)送到路由器ActorRef 的消息可以直接被路由到routees,直接跳過了單線程的Router actor。
自定義Router
在你創(chuàng)建自己的Router之前,要考慮到普通的actor性能不如Router actor高,因此你的程序可以接受較低的消息吞吐量。
假設(shè)你想獲得最大的性能,請參考
配置調(diào)度器
為子Pool創(chuàng)建調(diào)度器將從Props中取,在調(diào)度器中有描述
為了可以很容易地定義池中routee的調(diào)度器,你可以在配置的部署一節(jié)中定義內(nèi)聯(lián)調(diào)度器。

Router的“頭”,不能總在相同的調(diào)度器上運行,因為它處理各種各樣不同類型的消息,因此這個“頭”是個特殊的actor,不使用Props驗證的調(diào)度器。但是在RouterConfig中的?routerDispatcher 是標準Router actor默認調(diào)度器,可以在Router 的構(gòu)造器和工廠方法中配置。 即使是自定義的Router也必須要實現(xiàn)默認調(diào)度器。