Scala 使用条件语句发送到不同的接收方参与者

Scala 使用条件语句发送到不同的接收方参与者,scala,akka,Scala,Akka,我是akka的新手,需要一些帮助来解决这个问题,如果我叫错了名字,也请原谅我。我有一个json文档,需要根据特定字段是否为真,转到两个接收者之一。我已经能够使用以下工具向各种单一消费者发布: Source(publisher).map{cmd => println("****************** Pick up message"+cmd) val cmdAst = cmd.toString.parseJson cmdAst.convertTo[FormAdded] }.

我是akka的新手,需要一些帮助来解决这个问题,如果我叫错了名字,也请原谅我。我有一个json文档,需要根据特定字段是否为真,转到两个接收者之一。我已经能够使用以下工具向各种单一消费者发布:

Source(publisher).map{cmd =>
  println("****************** Pick up message"+cmd)
  val cmdAst = cmd.toString.parseJson
  cmdAst.convertTo[FormAdded]
}.runWith(Sink.actorSubscriber(ProcessorActor.props))
我已尝试插入如下条件语句:

var actorSubscriber = ProcessorActor.props

Source(publisher).map { cmd =>
  println("****************** Pick up message" + cmd)
  val cmdAst = cmd.toString.parseJson
  cmdAst.convertTo[FormAdded]
    if (cmdAst.convertTo[FormAdded].toLocation.toString().equalsIgnoreCase("Main")) {
      actorSubscriber = EventActor.props
    } else actorSubscriber = ProcessorActor.props
}.runWith(Sink.actorSubscriber(actorSubscriber))
一切编译和运行都没有错误,但当表单提交时,它从不拾取,只是超时

我还尝试通过以下方式直接接收订户,以获得相同的结果:

Source(publisher).map { cmd =>
  println("****************** Pick up message" + cmd)
  val cmdAst = cmd.toString.parseJson
  cmdAst.convertTo[FormAdded]
    if (cmdAst.convertTo[FormAdded].toLocation.toString().equalsIgnoreCase("Main")) {
      (Sink.actorSubscriber(EventActor.props)
    } else (Sink.actorSubscriber(ProcessorActor.props)}

谢谢

您不能让接收器依赖于元素:流已经存在,并且在运行并开始从源获取元素之前包含固定的
接收器
(它们中没有一个可以满足您的要求)


您可以做的是使用一个
Sink
,它将根据元素将接收到的元素转发给不同的参与者。例如,您可以编写一个
DispatchActor
,查看它接收的消息,并将它们发送到
EventActor
ProcessorActor
,然后与
Sink.actorSubscriber(DispatchActor.props)一起使用

您不能使接收器依赖于元素:流在运行并开始从源获取元素之前已经存在并包含固定的
接收器
(它们中没有一个可以满足您的要求)


您可以做的是使用一个
Sink
,它将根据元素将接收到的元素转发给不同的参与者。例如,您可以编写一个
DispatchActor
,查看它接收到的消息,并将它们发送到
EventActor
ProcessorActor
,然后将其与
Sink.actorSubscriber(DispatchActor.props)
一起使用
广播和两个不同的接收器来实现。下面是一个使用整数的
并将其拆分为两个不同汇的示例,一个用于偶数,另一个用于赔率

import scala.concurrent.duration._
import akka.stream.scaladsl.{Source, Sink, Flow, FlowGraph, Broadcast, Merge}
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer

object TwoSinks extends App {

  implicit val actorSystem = ActorSystem()
  implicit val materializer = ActorMaterializer()

  FlowGraph.closed() { implicit builder: FlowGraph.Builder[Unit] =>
    import FlowGraph.Implicits._

    val source = Source(1 to 10)

    //beam splitter
    val bcast = builder.add(Broadcast[Int](2))

    def isEven(i : Int) = i % 2 == 0

    val evenFilter = Flow[Int].filter(isEven)
    val oddFilter  = Flow[Int].filter(!isEven(_))

    val evenSink = Sink.foreach[Int](i => println(s"evenSink received : $i"))    
    val oddSink  = Sink.foreach[Int](i => println(s" oddSink received : $i"))

    source ~> bcast ~> evenFilter ~> evenSink
              bcast ~> oddFilter  ~> oddSink
  }.run()

  import actorSystem.dispatcher
  actorSystem.scheduler.scheduleOnce(10 seconds){actorSystem.shutdown()}
}//end object TwoSinks
您可以很容易地扩展这个示例,以使用在接收器中使用Actors而不是println的。使用您的特定代码:

FlowGraph.closed() {implicit builder: FlowGraph.Builder[Unit] =>
  import FlowGraph.Implicits._

  def pickupMsg(cmd : String) = 
    {println(s"****************** Pick up message: $cmd") ; cmd}

  //I don't know the type of cmdAst so I'm using "CmdAstType"
  type CmdAstType = ???

  def convert(cmd : String) : CmdAstType = {
    val cmdAst = cmd.toString.parseJson
    cmdAst.convertTo[FormAdded]
    cmdAst
  }//end def convert

  val source = Source(publisher).map(pickupMsg).map(convert)

  def isEvent(cmdAst : CmdAstType) : Boolean = cmdAst.convertTo[FormAdded]
                                                     .toLocation 
                                                     .toString()
                                                     .equalsIgnoreCase("Main")

  val eventFilter     = Flow[CmdAstType].filter(isEvent)
  val processorFilter = Flow[CmdAstType].filter(!isEvent(_))

  val eventSink     = Sink.actorSubscriber[CmdAstType](EventActor.props)
  val processorSink = Sink.actorSubscriber[CmdAstType](ProcessorActor.props)

  val bcast = builder.add(Broadcast[CmdAstType](2))

  source ~> bcast ~> eventFilter     ~> eventSink
            bcast ~> processorFilter ~> processorSink

}//end FlowGraph.closed()

这可以通过使用
广播和两个不同的接收器来实现。下面是一个使用整数的
并将其拆分为两个不同汇的示例,一个用于偶数,另一个用于赔率

import scala.concurrent.duration._
import akka.stream.scaladsl.{Source, Sink, Flow, FlowGraph, Broadcast, Merge}
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer

object TwoSinks extends App {

  implicit val actorSystem = ActorSystem()
  implicit val materializer = ActorMaterializer()

  FlowGraph.closed() { implicit builder: FlowGraph.Builder[Unit] =>
    import FlowGraph.Implicits._

    val source = Source(1 to 10)

    //beam splitter
    val bcast = builder.add(Broadcast[Int](2))

    def isEven(i : Int) = i % 2 == 0

    val evenFilter = Flow[Int].filter(isEven)
    val oddFilter  = Flow[Int].filter(!isEven(_))

    val evenSink = Sink.foreach[Int](i => println(s"evenSink received : $i"))    
    val oddSink  = Sink.foreach[Int](i => println(s" oddSink received : $i"))

    source ~> bcast ~> evenFilter ~> evenSink
              bcast ~> oddFilter  ~> oddSink
  }.run()

  import actorSystem.dispatcher
  actorSystem.scheduler.scheduleOnce(10 seconds){actorSystem.shutdown()}
}//end object TwoSinks
您可以很容易地扩展这个示例,以使用在接收器中使用Actors而不是println的。使用您的特定代码:

FlowGraph.closed() {implicit builder: FlowGraph.Builder[Unit] =>
  import FlowGraph.Implicits._

  def pickupMsg(cmd : String) = 
    {println(s"****************** Pick up message: $cmd") ; cmd}

  //I don't know the type of cmdAst so I'm using "CmdAstType"
  type CmdAstType = ???

  def convert(cmd : String) : CmdAstType = {
    val cmdAst = cmd.toString.parseJson
    cmdAst.convertTo[FormAdded]
    cmdAst
  }//end def convert

  val source = Source(publisher).map(pickupMsg).map(convert)

  def isEvent(cmdAst : CmdAstType) : Boolean = cmdAst.convertTo[FormAdded]
                                                     .toLocation 
                                                     .toString()
                                                     .equalsIgnoreCase("Main")

  val eventFilter     = Flow[CmdAstType].filter(isEvent)
  val processorFilter = Flow[CmdAstType].filter(!isEvent(_))

  val eventSink     = Sink.actorSubscriber[CmdAstType](EventActor.props)
  val processorSink = Sink.actorSubscriber[CmdAstType](ProcessorActor.props)

  val bcast = builder.add(Broadcast[CmdAstType](2))

  source ~> bcast ~> eventFilter     ~> eventSink
            bcast ~> processorFilter ~> processorSink

}//end FlowGraph.closed()


您能否扩展一下
接收器
以所需方式处理消息?在akka文档中有没有一个好的参考部分可以看,或者是一个执行编写一个新演员的动作的例子?我不是说编写一个新演员,而是说你可以编写一个新演员,并在一个
Sink
中使用它。
平衡[t]
扇出对这种情况不起作用吗?没有;它向其发送元素的流不依赖于元素本身,它只是试图平衡其订阅者之间的负载。您能否扩展
接收器
以所需方式处理消息?在akka文档中有没有一个好的参考部分可以看,或者是一个执行编写一个新演员的动作的例子?我不是说编写一个新演员,而是说你可以编写一个新演员,并在一个
Sink
中使用它。
平衡[t]
扇出对这种情况不起作用吗?没有;它向其中发送元素的流并不依赖于元素本身,它只是试图平衡其订阅者之间的负载。这有一个轻微的缺点:必须在两个(或无论有多少个)过滤器中评估条件,如果代价高昂,这可能是个问题。但是,这是可以解决的:使用
Flow.map(x=>(x,条件(x))
广播之前
并将
过滤器
广播
接收器
之间的另一个
映射
结合起来。@AlexeyRomanov我完全同意,但我认为
Flow.map
解决方案会稍微模糊我在回答中试图解释的一般结构。就像往常一样s、 魔鬼就在细节中…嘿,伙计们,非常感谢你们的帮助。我已经应用了@RamonJ.RomeroyVigil的例子,我看到它进入println语句
*************************获取消息:{}
,但它在那里停滞了。我得到的下一行是:
[DEBUG][11/13/2015 09:34:08.617][默认akka.actor.default-dispatcher-6][akka://default/user/$a/flow-3-4-publisherSource-prefixAndTail]取消akka.stream.impl.MultistreamOutProcessor$SubstreamOutput@155cd6c6(之后:5000毫秒)
我是否需要更改接收器端的某些内容?在编写接收器参与者时必须小心谨慎。他们必须按需传递或使用策略。首先尝试使用基本的println接收器,看看这是否是问题所在……阅读文档以查看有关设置订户的正确说明:这有一个小小的缺点:条件离子必须在两个(或无论你有多少个)过滤器中进行计算,这可能是一个问题,如果它很昂贵的话。但是,这可以解决:使用
Flow.map(x=>(x,条件(x))
广播之前
并将
过滤器
广播
接收器
之间的另一个
映射
结合起来。@AlexeyRomanov我完全同意,但我认为
Flow.map
解决方案会稍微模糊我在回答中试图解释的一般结构。就像往常一样s、 魔鬼就在细节中…嘿,伙计们,非常感谢你们的帮助。我已经应用了@RamonJ.RomeroyVigil的例子,并且看到它进入println语句
*********************拾取消息:{