如何背压ActorPublisher

时间:2018-01-08 10:17:53

标签: scala akka reactive-programming akka-stream reactive-streams

我正在写几个样本来了解akka流和背压。我正在试图看一个缓慢的消费者背压是一个AkkaPublisher

我的代码如下。

class DataPublisher extends ActorPublisher[Int] {

  import akka.stream.actor.ActorPublisherMessage._

  var items: List[Int] = List.empty

  def receive = {
    case s: String =>
      println(s"Producer buffer size ${items.size}")
      if (totalDemand == 0)
        items = items :+ s.toInt
      else
        onNext(s.toInt)

    case Request(demand) =>
      if (demand > items.size) {
        items foreach (onNext)
        items = List.empty
      }
      else {
        val (send, keep) = items.splitAt(demand.toInt)
        items = keep
        send foreach (onNext)
      }


    case other =>
      println(s"got other $other")
  }
}

Source.fromPublisher(ActorPublisher[Int](dataPublisherRef)).runWith(sink)

其中,接收器是具有睡眠的用户,以模拟慢速消费者。并且发布者不断地生成数据。

- EDIT-- 我的问题是,当需求为0时,以编程方式缓冲数据。如何利用背压来减慢发布者的速度

这样的东西
throttledSource().buffer(10, OverflowStrategy.backpressure).runWith(throttledSink())

这不会影响发布者及其缓冲区继续运行。

谢谢, Sajith

2 个答案:

答案 0 :(得分:3)

不要使用ActorPublisher

首先,请勿使用ActorPublisher - 这是一个非常低级且已弃用的API 。我们决定弃用,因为用户不应该在Akka Streams中进行如此低级别的抽象。

其中一个棘手的问题正是你要问的问题 - 如果他们使用这个API ,那么处理backpressure完全掌握在编写ActorPublisher的开发人员手中。因此,您必须收到Request(n)消息,并确保从不发出更多信号,而不是您的请求。此行为在Reactive Streams Specification中指定,然后您必须正确实现。基本上,您已经暴露于Reactive Streams的所有复杂性(这是一个完整的规范,包含许多边缘情况 - 免责声明:我是开发Reactive Streams以及Akka Streams的一部分。)

显示如何在GraphStage中显示背压

其次,要构建自定义阶段,您应该使用为其设计的API:GraphStage。请注意,这样的舞台也是相当低的水平。通常情况下,Akka Streams的用户不需要编写自定义阶段,但是如果他们实现内置阶段无法提供的某些逻辑,那么编写自己的阶段是绝对的期望和罚款。

这是来自Akka代码库的简化过滤器实现:


case class Filter[T](p: T ⇒ Boolean) extends SimpleLinearGraphStage[T] {
  override def initialAttributes: Attributes = DefaultAttributes.filter

  override def toString: String = "Filter"

  override def createLogic(inheritedAttributes: Attributes): GraphStageLogic =
    new GraphStageLogic(shape) with OutHandler with InHandler {

    override def onPush(): Unit = {
      val elem = grab(in)
      if (p(elem)) push(out, elem)
      else pull(in)
    }

    // this method will NOT be called, if the downstream has not signalled enough demand!
    // this method being NOT called is how back-pressure manifests in stages
    override def onPull(): Unit = pull(in)

    setHandlers(in, out, this)
  }
}

正如您所看到的,您可以获得简单的回调,例如onPushonPull,而不是自己实现整个Reactive Streams逻辑和规则(这很难)。 Akka Streams处理需求管理,如果下游已发出需求信号,它将自动调用onPull,如果没有需求,它将不会调用它 - 这意味着下游正在向此阶段施加背压。

答案 1 :(得分:2)

这可以通过中间Flow.buffer

来完成
With()