我有一个内置广播和zip的流程图。如果某个东西(无论它是什么)在这个流程中失败了,我想删除传递给它的有问题的元素并恢复。我提出了以下解决方案:
val flow = Flow.fromGraph(GraphDSL.create() { implicit builder =>
import GraphDSL.Implicits._
val dangerousFlow = Flow[Int].map {
case 5 => throw new RuntimeException("BOOM!")
case x => x
}
val safeFlow = Flow[Int]
val bcast = builder.add(Broadcast[Int](2))
val zip = builder.add(Zip[Int, Int])
bcast ~> dangerousFlow ~> zip.in0
bcast ~> safeFlow ~> zip.in1
FlowShape(bcast.in, zip.out)
})
Source(1 to 9)
.via(flow)
.withAttributes(ActorAttributes.supervisionStrategy(Supervision.restartingDecider))
.runWith(Sink.foreach(println))
我希望它能打印出来:
(1,1)
(2,2)
(3,3)
(4,4)
(5,5)
(6,6)
(7,7)
(8,8)
(9,9)
但是,它只会打印死锁:
(1,1)
(2,2)
(3,3)
(4,4)
我们已经完成了一些调试,结果是它将“恢复”策略应用于其子节点,这导致dangerousFlow
在失败后恢复,从而要求bcast
中的元素。在bcast
需要另一个元素之前,safeFlow
不会发出元素,这实际上从未发生过(因为它正在等待来自zip
的需求)。
有没有办法恢复图表,无论其中一个阶段内出了什么问题?
答案 0 :(得分:3)
我认为你很好地理解了这个问题。您看到,当您的元素5
崩溃dangerousFlow
时,您还应该停止正在经历5
的元素safeFlow
,因为如果它到达zip
阶段,你有你描述的问题。我不知道如何在broadcast
和zip
阶段之间解决您的问题,但是如何进一步推动问题,哪个更容易处理?
考虑使用以下dangerousFlow
:
import scala.util._
val dangerousFlow = Flow[Int].map {
case 5 => Failure(new RuntimeException("BOOM!"))
case x => Success(x)
}
即使出现问题,dangerousFlow
仍会发出数据。然后,您可以像当前一样zip
,只需添加collect
阶段作为图表的最后一步。在流程中,这看起来像:
Flow[(Try[Int], Int)].collect {
case (Success(s), i) => s -> i
}
现在,如果你真的希望输出(5, 5)
元组,请使用以下内容:
Flow[(Try[Int], Int)].collect {
case (Success(s), i) => s -> i
case (_, i) => i -> i
}