如何限制Akka Stream每秒只执行一次消息并发送一次消息?

时间:2016-03-25 06:38:21

标签: akka rate rate-limiting akka-stream

我有一个Akka Stream,我希望流大约每秒都向下发送消息。

我尝试了两种方法来解决这个问题,第一种方法是让生产者在流的开头只在Continue消息进入这个actor时每秒发送一次消息。

// When receive a Continue message in a ActorPublisher // do work then... if (totalDemand > 0) { import scala.concurrent.duration._ context.system.scheduler.scheduleOnce(1 second, self, Continue) }

这可以工作一段时间然后大量的Continue消息出现在ActorPublisher actor中,我假设(猜测但不确定)来自下游通过反压请求消息,因为下游可以快速消耗但上游不生成速度快。所以这种方法失败了。

我尝试的另一种方法是通过背压控制,我在流末尾的MaxInFlightRequestStrategy上使用了ActorSubscriber来将消息数量限制为每秒1个。这是有效的,但是一次进入的消息大约是三个左右,而不是一次一个。似乎背压控制没有立即改变进入的消息的速率,或者消息已经在流中排队并等待处理。

所以问题是,我怎样才能拥有一个只能每秒处理一条消息的Akka Stream?

我发现MaxInFlightRequestStrategy是一种有效的方法,但我应该将批量大小设置为1,其批量大小默认为5,这导致了我发现的问题。现在我正在查看提交的答案,这也是解决问题的一种过于复杂的方式。

1 个答案:

答案 0 :(得分:12)

您可以将元素放在限制流中,这会对快速来源施加压力,或者您可以使用tickzip的组合。

第一个解决方案是这样的:

val veryFastSource =
  Source.fromIterator(() => Iterator.continually(Random.nextLong() % 10000))

val throttlingFlow = Flow[Long].throttle(
  // how many elements do you allow
  elements = 1,
  // in what unit of time
  per = 1.second,
  maximumBurst = 0,
  // you can also set this to Enforcing, but then your
  // stream will collapse if exceeding the number of elements / s
  mode = ThrottleMode.Shaping
)

veryFastSource.via(throttlingFlow).runWith(Sink.foreach(println))

第二种解决方案是这样的:

val veryFastSource =
  Source.fromIterator(() => Iterator.continually(Random.nextLong() % 10000))

val tickingSource = Source.tick(1.second, 1.second, 0) 

veryFastSource.zip(tickingSource).map(_._1).runWith(Sink.foreach(println))