有两个输入流,都产生定义为
的对象实例case class ReplayData(timestamp:Long, payload:Any)
流1
1,有效载荷1
1000,有效载荷3
流2
400,有效负载2
1500,有效负载4
我想实现重播机制, 会按我在每个元素上按时间戳排序的顺序将元素推到下游
它将模拟生产中的实时场景。
此机制需要服从消息之间的延迟,例如第一个消息发送是有效负载1(它的起点),来自Stream2的有效负载2应该在400毫秒(下一个消息时间戳和初始消息时间戳之间的差)之后发送。
我可以使用DelayedQueue轻松做到这一点,SO thread
中对此用法进行了说明Delayed元素的无限制阻塞队列,其中一个元素 只能在延迟时间到期后才能拍摄。
队列的头是延迟已过期的Delayed元素 过去最远的。如果没有延迟,就没有头和脚 poll将返回null。
当元素的getDelay(TimeUnit.NANOSECONDS)发生过期 方法返回的值小于或等于零。即使 未过期的元素无法使用take或poll删除,它们是 否则视为正常元素。
例如,size方法返回过期和过期的计数 未过期的元素。此队列不允许空元素。确实 不允许使用空元素。
我正在尝试弄清楚如何在Akka流中执行此操作,但是很难找到可以为我解决此问题的方法。
我当时看着mergeSorted是合并两个流并根据某些功能对其进行排序的一种方式。
似乎或多或少地适合于基于某些自定义功能进行排序的目的。
我不确定如何根据时间戳属性处理元素之间的延迟。
使用普通的旧AKKA,我可以使用调度程序读取数据,对数据进行排序并安排时间流逝后要发送的每个元素。
答案 0 :(得分:1)
我不记得akka流中的任何内容,这些内容可能会延迟开箱即用的消息,并且会为每条消息自定义延迟。毕竟,akka-streams背后的想法是反应式编程。我只知道2种方法通常可以解决您的问题(假设您已经合并了2个来源)
Flow.mapAsync-在这种情况下,延迟一段时间后返回Future
完全是您的事。例如:
import java.time.LocalDateTime
import java.util.concurrent.Executors
import akka.NotUsed
import akka.actor.ActorSystem
import akka.pattern.after
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Source
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
import scala.concurrent.{ExecutionContext, Future}
object Application extends App {
implicit val sys: ActorSystem = ActorSystem()
implicit val mat: ActorMaterializer = ActorMaterializer()
case class SomeEntity(time: Int, value: Int)
val source: Source[SomeEntity, NotUsed] = Source(List(100, 200, 400, 1000, 1100, 1400)).map(i => SomeEntity(i, i * i + 3))
val ec: ExecutionContext = ExecutionContext.fromExecutor(Executors.newFixedThreadPool(10))
val scheduler = sys.scheduler
val f = source
.mapAsync(10)(se => after(se.time.milliseconds, scheduler)(Future.successful(se))(ec))
.runForeach(se => println(s"${LocalDateTime.now()} -> $se"))
f.onComplete(_ => sys.terminate())
}
可能会发生您的用例(毕竟是模拟)实际上并不那么严格的情况,因此您可以使用Flow.throttle。它不是第一种解决方案那样简单,精确,但是它的性能要高得多,因为它使用了一些轻量级的存储桶模型来控制物品的输出速度。
import java.time.LocalDateTime
import akka.NotUsed
import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Source
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._
object Application extends App {
implicit val sys: ActorSystem = ActorSystem()
implicit val mat: ActorMaterializer = ActorMaterializer()
case class SomeEntity(time: Int, value: Int)
val source: Source[SomeEntity, NotUsed] = Source(List(100, 200, 400, 1000, 1100, 1400, 1400, 1500, 1900, 2500, 2700)).map(i => SomeEntity(i, i * i + 3))
val future = source.throttle(cost = 1, per = 1.millisecond, _.time).runForeach(se => {
println(s"${LocalDateTime.now()} -> $se")
})
future.onComplete(_ => sys.terminate())
}