【问题标题】:How to determine Akka actor/supervisor hierarchy?如何确定 Akka 演员/主管层次结构?
【发布时间】:2015-04-21 13:47:12
【问题描述】:

我是 Akka 的新手(Java lib,v2.3.9)。我正在尝试关注supervisor hierarchy best practices,但由于这是我的第一个 Akka 应用程序,我在某处遇到了心理障碍。

在我的第一个 Akka 应用程序(实际上是一个旨在跨多个应用程序重用的库)中,来自外部世界的输入表现为传递给参与者的 Process 消息。使用我的应用程序的开发人员将提供一个基于文本的配置文件,最终配置哪些参与者会被发送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) .至于哪些 Actor 还活着/在线来处理这些Process 消息完全是配置驱动的。最后,应用程序每次收到Input 时,都会将其注入到Process 消息中,并将Process 消息发送给所有已配置/活动的actor。

有了这个作为给定的背景故事/设置,我无法确定“演员/主管层次结构”需要是什么。在我的用例中,似乎所有参与者都是真正平等的,他们之间没有监督结构。 StormTrooper 只是接收Process 消息,如果该类型的参与者被配置为存在。其他actor子类也一样。

我在这里完全错过了什么吗?如果所有参与者都是平等的并且层次结构本质上是“扁平”/水平的,我该如何定义监督层次结构(出于容错目的)?

【问题讨论】:

  • 借用 Viktor Klang 的一个思考过程,想想如果你只有普通人而不是电脑你会怎么做,然后把每个人想象成一个演员。哪些人监督哪些人?
  • 谢谢@Ryan (+1) - 如果我有真正的人作为演员,而不是电脑,我会有一个带有StormTrooperDarthVader 和里面的Emperor 的牢房.当有人想向这个牢房的居民发送信息时,他们必须在一张纸上写下相同的信息,每人一张纸。如果他们希望同时向StormTrooperEmperor 发送消息,他们会在两张纸上写下完全相同的消息。然后他们会将所有/任何文件交给我,我会将信息传递给适用的各方。
  • 换句话说,这里仍然没有主管层次结构。 :-)

标签: java akka actor fault-tolerance akka-supervision


【解决方案1】:

如果您希望为每个演员实例化不超过一个实例 - 您可能需要 SenatorPalpatine 来监督这三个。如果你可以说不止一个StormTrooper - 你可能想让JangoFett 演员负责创建(甚至可能杀死)他们,一些router 也是不错的选择(它会自动监督他们)。这还将使您能够在一个失败时重新启动所有士兵 (OneForAllStrategy)、广播、保存一些通用统计数据等。

带有路由器的示例(伪 Scala):

//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 p@Process => troopers ! p; vader ! p
         case t@Terminted => println(t)
    }
 }

根据标准akka-config 创建广播池。我还展示了您可以分别为它们自定义监督策略。

如果您希望某些演员因某种原因忽略消息 - 只需在演员内部实现此逻辑,例如:

class Vader extends Actor {
    def receive {
        case p@Process => ...
        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

如果您想从配置中控制异构广播,您也可以使用与 trooper 路由器相同的方式为 palpatine 创建广播器(只需使用组而不是池):

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 p@Process => broadcaster ! p
   }
}

只需将 vader 从routees.paths 中排除,以使他不会收到Process 消息。

附: Actor 永远不会孤单 - 总有 Guardian Actor(参见The Top-Level Supervisors),它会在出现异常时关闭整个系统。所以无论如何SenatorPalpatine 可能真的会成为你的救星。

P.S.2 context.actorSelection("palpatine/*") 实际上允许您向所有孩子发送消息(作为广播池和组的替代方案),因此您不需要在里面设置一组。

【讨论】:

  • 谢谢@dk14 (+1) - 那么SenatorPalpatine 看起来像什么,代码方面?他会只包含Set&lt;ActorRef&gt;(由JSON 配置文件驱动),遍历该集合并向列表中的所有参与者发送Process 消息吗?
  • 不,你不需要Set&lt;ActorRef&gt;在这里,如果你想广播-context.actorSelection("../*")!味精(来自帕尔帕廷)或system.actorSelection("palpatine/*")! msg` 将完成这项工作(在 Scala 中)。
  • @smeeb 我已经更新了 Scala-examples,这个解决方案是基于路由器和 akka-config,而不是自定义配置
  • 它创建一个名为“palpatine”的Palpatine 实例并向其发送Process 消息。
  • 顺便说一句,Palpatine 可能与这里的Emperor 不同,实际上Emperor 可能会破坏整个隐喻,如果您认为帝国可能没有他或有几个皇帝:)。所以这只是Palpatine 的另一个孩子(从 SW 的角度来看应该是相反的 - 我知道 :),只是忘了他)
【解决方案2】:

基于your comment,您仍然希望Master 演员复制和分发Processes。从概念上讲,您不会让用户(或任何生成输入的东西)为每个参与者提供一次相同的输入。他们只会提供一次消息,然后您(或 Master 演员)将根据需要复制该消息并将其发送给每个相应的子演员。

正如dk14's answer 中所讨论的,这种方法具有提高容错能力的额外好处。

【讨论】:

  • 感谢@Luke Willis (+1) - 请参阅我的最后一条评论,要求从下面的 dk14 获得一个简单的代码 sn-p - 我有同样的问题要问你!
猜你喜欢
  • 1970-01-01
  • 2013-05-14
  • 2014-12-20
  • 1970-01-01
  • 2018-06-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多