我正尝试通过以下方式使用Akka流构建pub sub总线:
发布者为主题添加源流,订阅者指定主题并获取该主题的所有内容。但是,主题可以由多个发布者发布,发布者和订阅者都可以随时加入。
我想到的是合并所有源,然后将过滤后的源返回给订户。
但是,由于发布者可以随时加入,因此可以在订阅后添加来源,订阅者需要从中获取数据,就像该主题的任何其他已发布数据一样。
有没有一种方法可以动态地管理流与源的合并,从而使以下内容成立:
发布(主题:字符串,消息:来源[T]) subscription(topic:字符串):来源[T]
这样,无论何时添加发布者,主题订阅者都将在订阅后将所有消息发布到与该主题相关的任何来源。
也很高兴听到其他方法。
谢谢, Z
答案 0 :(得分:1)
您可能想看看以下Akka文档:使用dynamic pub-sub service
和MergeHub
构建BroadcastHub
。
这里是将MergeHub
和BroadcastHub
分别用作动态扇入和扇出结的示例代码。
想法是将MergeHub
与BroadcastHub
连接以通过Flow.fromSinkAndSource
以Flow的形式形成一个发布订阅频道:
val (bfSink, bfSource) = MergeHub.source[String](perProducerBufferSize).
toMat(BroadcastHub.sink[String](bufferSize))(Keep.both).
run
val busFlow: Flow[String, String, NotUsed] = Flow.fromSinkAndSource(bfSink, bfSource)
请注意,以上代码段中的Keep.both
从(Sink[T, NotUsed], Source[T, NotUsed])
和MergeHub.source[T]
生成具有以下方法签名的元化值BroadcastHub.sink[T]
的元组:
object MergeHub {
def source[T](perProducerBufferSize: Int): Source[T, Sink[T, NotUsed]] = // ...
// ...
}
object BroadcastHub {
def sink[T](bufferSize: Int): Sink[T, Source[T, NotUsed]] = // ...
// ...
}
下面是一个简单的发布订阅频道busFlow
的示例代码(类似于Akka文档中的示例):
import akka.actor.ActorSystem
import akka.stream._
import akka.stream.scaladsl._
import akka.NotUsed
implicit val system = ActorSystem("system")
implicit val materializer = ActorMaterializer()
implicit val ec = system.dispatcher
val (bfSink, bfSource) = MergeHub.source[String](perProducerBufferSize = 32).
toMat(BroadcastHub.sink[String](bufferSize = 256))(Keep.both).
run
// Optional: avoid building up backpressure when there is no subscribers
bfSource.runWith(Sink.ignore)
val busFlow: Flow[String, String, NotUsed] = Flow.fromSinkAndSource(bfSink, bfSource)
测试busFlow
:
Source(101 to 103).map(i => s"Batch(A)-$i").
delay(2.seconds, DelayOverflowStrategy.backpressure).
viaMat(busFlow)(Keep.right).
to(Sink.foreach{ case s: String => println("Consumer(1)-" + s) }).
run
Source(104 to 105).map(i => s"Batch(B)-$i").
viaMat(busFlow)(Keep.right).
to(Sink.foreach{ case s: String => println("Consumer(2)-" + s) }).
run
// Consumer(2)-Batch(B)-104
// Consumer(2)-Batch(B)-105
// Consumer(1)-Batch(B)-104
// Consumer(1)-Batch(B)-105
// Consumer(1)-Batch(A)-101
// Consumer(1)-Batch(A)-102
// Consumer(2)-Batch(A)-101
// Consumer(1)-Batch(A)-103
// Consumer(2)-Batch(A)-102
// Consumer(2)-Batch(A)-103
用作发布订阅通道,busFlow
的输入通过bfSink
发布给所有订阅者,而其输出则通过bfSource
流所有发布的元素。例如:
val p1 = Source.tick[Int](0.seconds, 5.seconds, 5).map(_.toString)
p1.runWith(bfSink)
val p2 = Source.tick[Int](2.seconds, 10.seconds, 10).map(_.toString)
p2.runWith(bfSink)
val s1 = bfSource
s1.runForeach(x => println(s"s1 --> $x"))
val s2 = bfSource
s2.runForeach(x => println(s"s2 --> $x"))
// s1 --> 5
// s2 --> 5
// s1 --> 10
// s2 --> 10
// s2 --> 5
// s1 --> 5
// s2 --> 5
// s1 --> 5
// s1 --> 10
// s2 --> 10
// s2 --> 5
// s1 --> 5
// ...
其他可能感兴趣的相关主题包括KillSwitch
(用于流完成控制)和PartitionHub
(用于将Stream元素从给定生产者路由到一组动态消费者)。
答案 1 :(得分:0)
这就是我最终要做的。发布者和订阅者都可以消失,并且无论订阅者何时加入以及发布者何时加入,订阅者都应能够(按主题)查看其订阅的所有已发布消息,而与订阅时处于活动状态的发布者无关。制作。欢迎发表评论。
def main(args: Array[String]): Unit = {
val actorSystem = ActorSystem("test")
val materializerSettings = ActorMaterializerSettings(actorSystem)
implicit val materializer = ActorMaterializer(materializerSettings)(actorSystem)
implicit val ec: ExecutionContext = actorSystem.dispatcher
val (queue, pub) = Source.queue[Int](100, akka.stream.OverflowStrategy.dropHead).toMat(Sink.asPublisher(true))(Keep.both).run()
val p1 = Source.tick[Int](0.seconds, 5.seconds, 5)
p1.runForeach(x=> {queue.offer(x)})
val p2= Source.tick[Int](2.seconds,10.seconds, 10)
p2.runForeach(x=> queue.offer(x))
val s1 = Source.fromPublisher(pub)
s1.runForeach(x=> println(s"s1 =======> ${x}"))
val s2 = Source.fromPublisher(pub)
s2.runForeach(x=> println(s"s2 =======> ${x}"))
}