Akka Stream - Pausable GraphStage(Akka 2.5.7)

时间:2017-12-03 21:53:41

标签: scala akka akka-stream

我想编写一个GraphStage,可以通过发送来自其他actor的消息暂停/取消暂停。

下面的代码显示了一个生成随机数的简单GraphStage。当阶段实现后,GraphStageLogic会向主管发送包含preStart()的消息(在StageActor内)。主管保持舞台的ActorRef,因此可以用来控制舞台。

object RandomNumberSource {
  case object Pause
  case object UnPause
}

class RandomNumberSource(supervisor: ActorRef) extends GraphStage[SourceShape[Int]] {

  val out: Outlet[Int] = Outlet("rnd.out")

  override val shape: SourceShape[Int] = SourceShape(out)

  override def createLogic(inheritedAttributes: Attributes): GraphStageLogic = {
    new RandomNumberSourceLogic(shape)
  }

  private class RandomNumberSourceLogic(shape: Shape) extends GraphStageLogic(shape) with StageLogging {

    lazy val self: StageActor = getStageActor(onMessage)

    val numberGenerator: Random = Random
    var isPaused: Boolean = true

      override def preStart(): Unit = {
        supervisor ! AssignStageActor(self.ref)
      }

      setHandler(out, new OutHandler {
        override def onPull(): Unit = {
          if (!isPaused) {
            push(out, numberGenerator.nextInt())
            Thread.sleep(1000)
          }
        }
      })

      private def onMessage(x: (ActorRef, Any)): Unit =
      {
        x._2 match {
          case Pause =>
            isPaused = true
            log.info("Stream paused")
          case UnPause =>
            isPaused = false
            getHandler(out).onPull()
            log.info("Stream unpaused!")
          case _ =>
        }
      }
    }
}

这是一个非常简单的主管演员实现:

object Supervisor {
  case class AssignStageActor(ref: ActorRef)
}

class Supervisor extends Actor with ActorLogging {

  var stageActor: Option[ActorRef] = None

  override def receive: Receive = {

    case AssignStageActor(ref) =>
      log.info("Stage assigned!")
      stageActor = Some(ref)
      ref ! Done

    case Pause =>
      log.info("Pause stream!")
      stageActor match {
        case Some(ref) => ref ! Pause
        case _ =>
      }

    case UnPause =>
      log.info("UnPause stream!")
      stageActor match {
        case Some(ref) => ref ! UnPause
        case _ =>
      }
  }
}

我正在使用以下应用程序来运行流:

object Application extends App {

  implicit val system = ActorSystem("my-actor-system")
  implicit val materializer = ActorMaterializer()

  val supervisor = system.actorOf(Props[Supervisor], "supervisor")

  val sourceGraph: Graph[SourceShape[Int], NotUsed] = new RandomNumberSource(supervisor)
  val randomNumberSource: Source[Int, NotUsed] = Source.fromGraph(sourceGraph)

  randomNumberSource.take(100).runForeach(println)

  println("Start stream by pressing any key")

  StdIn.readLine()

  supervisor ! UnPause

  StdIn.readLine()

  supervisor ! Pause

  StdIn.readLine()

  println("=== Terminating ===")
  system.terminate()
}

当应用程序启动阶段ia处于“暂停”状态并且不产生任何数字时。当我按下一个键时,我的舞台开始发出数字。但我的问题是,在启动后发送到舞台的所有消息都将被忽略。我无法暂停舞台。

我有兴趣根据从演员那里收到的消息来改变舞台的行为,但是我找到的所有例子都将演员的消息传递给流。

是否有人猜到我的代码无效或者想知道如何构建这样的GraphStage

非常感谢!

1 个答案:

答案 0 :(得分:1)

Akka Stream Contrib项目有一个Valve阶段,它表示可以暂停和恢复流的值。来自此类的Scaladoc:

  

实现ValveSwitch的未来,它提供了一个方法翻转,用于停止或重新启动通过舞台的元素流。只要阀门关闭,它就会反压。

例如:

val (switchFut, seqSink) = Source(1 to 10)
  .viaMat(new Valve(SwitchMode.Close))(Keep.right)
  .toMat(Sink.seq)(Keep.both)
  .run()

switchFutFuture[ValveSwitch],由于开关最初关闭,阀门背压并且没有任何物质向下游排放。打开阀门:

switchFut.onComplete {
  case Success(switch) =>  
    switch.flip(SwitchMode.Open) // Future[Boolean]
  case _ =>
    log.error("the valve failed")
}

更多示例位于ValveSpec