2015-04-21 31 views
2

我是Akka的新手(Java lib,v2.3.9)。我試圖遵循supervisor hierarchy best practices,但由於這是我的第一個Akka應用程序,我在某處遇到了精神障礙。如何確定Akka actor/supervisor層次結構?

在我的第一個Akka應用程序(真正意圖在多個應用程序中重複使用的庫)中,來自外部世界的輸入顯示爲傳遞給actor的Process消息。使用我的應用程序的開發人員將提供一個基於文本的配置文件,最終配置哪些參與者發送實例,哪些不參與實例。換句話說,說這些都是我的演員類:

// Groovy pseudo-code 
class Process { 
    private final Input input 

    Process(Input input) { 
     super() 
     this.input = deepClone(input) 
    } 

    Input getInput() { 
     deepClone(this.input) 
    } 
} 

class StormTrooper extends UntypedActor { 
    @Override 
    void onReceive(Object message) { 
     if(message instanceof Process) { 
      // Process the message like a Storm Trooper would. 
     } 
    } 
} 

class DarthVader extends UntypedActor { 
    @Override 
    void onReceive(Object message) { 
     if(message instanceof Process) { 
      // Process the message like Darth Vader would. 
     } 
    } 
} 

class Emperor extends UntypedActor { 
    @Override 
    void onReceive(Object message) { 
     if(message instanceof Process) { 
      // Process the message like the Emperor would. 
     } 
    } 
} 

// myapp-config.json -> where the actors are configured, along with other 
// app-specific configs 
{ 
    "fizzbuzz": "true", 
    "isYosemite": "false", 
    "borderColor": "red", 
    "processors": [ 
     "StormTrooper", 
     "Emperor" 
    ] 
} 

正如你可以在配置文件中看到,只有StormTrooperEmperor選擇接受Process消息。最終結果是零(0)DarthVader演員正在創建。這也是我的意圖,這將導致正在作出一個Set<ActorRef>可被填充了StormTrooperEmperor,像這樣的應用程序:

class SomeApp { 
    SomeAppConfig config 

    static void main(String[] args) { 
     String configFileUrl = args[0] // Nevermind this horrible code 

     // Pretend here that configFileUrl is a valid path to 
     // myapp-config.json. 

     SomeApp app = new SomeApp(configFileUrl) 
     app.run() 
    } 

    SomeApp(String url) { 
     super() 

     config = new SomeAppConfig(url) 
    } 

    void run() { 
     // Since the config file only specifies StormTrooper and 
     // Emperor as viable processors, the set only contains instances of 
     // these ActorRef types. 
     Set<ActorRef> processors = config.loadProcessors() 
     ActorSystem actorSystem = config.getActorSystem() 

     while(true) { 
      Input input = scanForInput() 
      Process process = new Process(input) 

      // Notify each config-driven processor about the 
      // new input we've received that they need to process. 
      processors.each { 
       it.tell(process, Props.self()) // This isn't correct btw 
      } 
     } 
    } 
} 

所以,你可以(希望)看,我們擁有所有這些演員(實際上,許多十幾個UntypedActor impls)處理Process消息(這反過來從一些來源捕獲Input)。至於哪些演員甚至還活着/在線來處理這些Process消息完全由配置驅動。最後,每次應用收到Input時,它都會被注入到Process消息中,並且該消息被髮送到所有已配置/生存的角色。

以此作爲給定的背景/設置,我無法確定「演員/監督員層級」應該是什麼。在我的使用案例中,所有參與者都是真正平等的,沒有監督結構。 StormTrooper只是簡單地收到Process消息,如果該類型的actor被配置爲存在。其他演員子類相同。

我完全錯過了什麼嗎?如果所有角色相同且層次結構本質上是「平坦」/水平的,如何定義監督層次結構(用於容錯目的)?

+1

借用Viktor Klang的一個流程,想想如果你只有普通人而不是電腦,那麼你會怎麼做,然後想象每個人都是演員。哪些人監督哪些人? – Ryan

+0

謝謝@Ryan(+1) - 如果我有真人作爲演員,而不是電腦,我會有一個擁有'StormTrooper','DarthVader'和'Emperor'的控制單元。當有人想發送信息給這個控股單位的居民時,他們必須在一張紙上寫下同樣的信息,每人一張紙。如果他們希望向「StormTrooper」和「Emperor」發送消息,他們會在兩張紙上寫下完全相同的消息。然後,他們會將所有/任何文件遞給我,我會將此信息傳達給適用的各方。 – smeeb

+0

換句話說,這裏仍然沒有管理員層次結構。 :-) – smeeb

回答

2

如果您想爲每個演員實例化不超過一個實例 - 您可能需要SenatorPalpatine來監督這三個實例。如果你可能有不止一個StormTrooper - 你可能想讓JangoFett負責創建(也許是殺死)他們,但一些router也是不錯的選擇(它會自動監督它們)。這也將讓你重新啓動所有士兵的能力,如果一個失敗(OneForAllStrategy),廣播,舉辦一些常用統計等

例(僞斯卡拉)能力與路由器:

//application.conf 
akka.actor.deployment { 
    /palpatine/vader { 
    router = broadcast-pool 
    nr-of-instances = 1 
    } 
    /palpatine/troopers { 
    router = broadcast-pool 
    nr-of-instances = 10 
    } 
} 

class Palpatine extends Actor { 
    import context._ 

    val troopers = actorOf(FromConfig.props(Props[Trooper], 
"troopers").withSupervisorStrategy(strategy) //`strategy` is strategy for troopers 

    val vader = actorOf(FromConfig.props(Props[Vader]), "vader") 

    override val supervisorStrategy = OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 1) //stategy for Palpatine's children (routers itself) 

    val strategy = OneForOneStrategy(maxNrOfRetries = 100, withinTimeRange = 1) //stategy for troopers 

    def receive = { 
     case [email protected] => troopers ! p; vader ! p 
     case [email protected] => println(t) 
    } 
} 

這就產生廣播池基於標準akka-config。我還表示你可以分別爲他們定製監督策略。

如果你想要一些演員由於某種原因忽略消息 - 剛剛實現這個邏輯裏面的演員,如:

class Vader extends Actor { 
    def receive { 
     case [email protected] => ... 
     case Ignore => context.become(ignore) //changes message handler to `ignore` 
    } 


    def ignore = { 
     case x => println("Ignored message " + x) 
     case UnIgnore => context.become(process)//changes message handler back 
    } 

} 

這將配置屏蔽選項/動態(否則它只是一個簡單的if)。您可以發送Ignore消息,基於一些配置的演員:

val listOfIgnorantPathes = readFromSomeConfig() 
context.actorSelection(listOfIgnoredPathes) ! Ignore 

您也可以以同樣的方式創建廣播的帕爾帕庭作爲士兵的路由器(只使用組,而不是池),如果要控制從異質廣播配置:

akka.actor.deployment { 
    ... //vader, troopers configuration 

    /palpatine/broadcaster { 
    router = broadcast-group 
    routees.paths = ["/palpatine/vader", "/palpatine/troopers"] 
    } 
} 

class Palpatine extends Actor { 
    ... //vader, troopers definitions 

    val broadcaster = actorOf(FromConfig.props(), "broadcaster") 

    def receive = { 
    case [email protected] => broadcaster ! p 
    } 
} 

剛剛從routees.paths排除維達讓他無法接受Process消息。

P.S.演員絕不是獨自一人 - 總是有監護人演員(參見The Top-Level Supervisors),如果發生異常,將會關閉整個系統。所以無論如何SenatorPalpatine可能真的成爲你的救援。

P.S.2 context.actorSelection("palpatine/*")實際上alows你要發送消息給所有的孩子(以替代廣播池和團體),所以你不需要有一組他們的內部。

+0

謝謝@ dk14(+1) - 那麼SenatorPalpatine看起來像什麼,代碼明智嗎?他是否會包含'Set '(由JSON配置文件驅動),遍歷該集合並將'Process'消息發送給列表中的所有actor? – smeeb

+1

不,你不需要'設置'這裏如果你想brodcast - 'context.actorSelection(「../*」)'! msg(來自palpatine)或'system.actorSelection(「palpatine/*」)'! msg'將完成這項工作(在Scala中)。 – dk14

+0

謝謝@ dk14(+1再次) - 但我仍然沒有看到「通過樹的森林」。對於賞金,我可以大膽地看到一個簡單的僞代碼示例:(1)'SenatorPalpatine'監督其他actor類型,(2)'SomeApp#run'將'Process'消息廣播給所有* *配置**(記住,也許只有*一些*的演員應該接收'進程'消息,也許一些被配置爲不接收它們)來接收它們?再次感謝您的幫助! – smeeb

1

根據your comment,您仍然希望Master actor複製並分發Process es。從概念上講,您不會讓用戶(或任何生成您的輸入)爲每個actor提供相同的輸入。他們只會提供一次消息,然後您(或Master演員)會根據需要複製消息,並將消息發送給每個適當的子演員。

正如在dk14's answer中討論的那樣,這種方法具有提高容錯性的額外好處。

+0

感謝@Luke Willis(+1) - 請參閱我的最後一條評論,請求一個簡單的代碼片段從下面的dk14 - 我有同樣的問題給你! – smeeb