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