Akka Streams中的Monadic短路

时间:2017-06-26 13:52:16

标签: scala akka akka-stream

我希望链接Flow形式的一系列a -> Try[b],其中每个连续阶段处理前一个Success的情况,并且最后的Sink通常会处理所有Failure

这个或类似的东西可以简洁地编码吗?它实际上是一个线性流,但我不确定每个阶段的广播和合并有多短。

1 个答案:

答案 0 :(得分:1)

解决此问题的一种方法是定义扇出阶段,将Try分为2个流,具体取决于其结果

  object PartitionTry {
    def apply[T]() = GraphDSL.create[FanOutShape2[Try[T], Throwable, T]]() { implicit builder ⇒
      import GraphDSL.Implicits._

      val success = builder.add(Flow[Try[T]].collect { case Success(a) ⇒ a })
      val failure = builder.add(Flow[Try[T]].collect { case Failure(t) ⇒ t })
      val partition = builder.add(Partition[Try[T]](2, _.fold(_ ⇒ 0, _ ⇒ 1)))

      partition ~> failure
      partition ~> success

      new FanOutShape2[Try[T], Throwable, T](partition.in, failure.out, success.out)
    }
  }

然后,您的通用流程可以提取Try并将Failure发送到选择的接收器,同时传递Success es

  object ErrorHandlingFlow {
    def apply[T, MatErr](errorSink: Sink[Throwable, MatErr]): Flow[Try[T], T, MatErr] = Flow.fromGraph(
      GraphDSL.create(errorSink) { implicit builder ⇒ sink ⇒
        import GraphDSL.Implicits._

        val partition = builder.add(PartitionTry[T]())

        partition.out0 ~> sink

        new FlowShape[Try[T], T](partition.in, partition.out1)
      }
    )
  }

下面的用法示例

  val source      : Source[String, NotUsed]           = Source(List("1", "2", "hello"))
  val convert     : Flow[String, Try[Int], NotUsed]   = Flow.fromFunction((s: String) ⇒ Try{s.toInt})
  val errorsSink  : Sink[Throwable, Future[Done]]     = Sink.foreach[Throwable](println)
  val handleErrors: Flow[Try[Int], Int, Future[Done]] = ErrorHandlingFlow(errorsSink)

  source.via(convert).via(handleErrors).runForeach(println)

请注意

  • 上面定义的两个阶段可以重复使用任何类型(一次编写,随处使用)
  • 此方法可以重用于其他类型类 - 例如Either等。