web-dev-qa-db-ja.com

Akka Streamsが私の例外を飲み込んでいるのはなぜですか?

の例外はなぜですか

import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.stream.scaladsl.Source

object TestExceptionHandling {
  def main(args: Array[String]): Unit = {
    implicit val actorSystem = ActorSystem()
    implicit val materializer = ActorMaterializer()(defaultActorSystem)

    Source(List(1, 2, 3)).map { i =>
      if (i == 2) {
        throw new RuntimeException("Please, don't swallow me!")
      } else {
        i
      }
    }.runForeach { i =>
      println(s"Received $i")
    }
  }
}

黙って無視? Received 1を出力した後、ストリームが停止していることがわかりますが、何もログに記録されません。 akka.log-config-on-start = onファイルにapplication.confを設定すると多くの出力が表示されるため、問題は一般にログ構成ではないことに注意してください。

19
Matthias Langer

現在、例外が適切にログに記録されるようにするカスタム_Supervision.Decider_を使用しています。これは、次のように設定できます。

_val decider: Supervision.Decider = { e =>
  logger.error("Unhandled exception in stream", e)
  Supervision.Stop
}

implicit val actorSystem = ActorSystem()
val materializerSettings = ActorMaterializerSettings(actorSystem).withSupervisionStrategy(decider)
implicit val materializer = ActorMaterializer(materializerSettings)(actorSystem)
_

また、 Vikor Klang で指摘されているように、上記の例では、例外は次の方法で「キャッチ」することもできます。

_Source(List(1, 2, 3)).map { i =>
  if (i == 2) {
    throw new RuntimeException("Please, don't swallow me!")
  } else {
    i
  }
}.runForeach { i =>
  println(s"Received $i")
}.onComplete {
  case Success(_) =>
    println("Done")
  case Failure(e) =>
    println(s"Failed with $e")
}
_

ただし、このアプローチでは役に立たないことに注意してください

_Source(List(1, 2, 3)).map { i =>
  if (i == 2) {
    throw new RuntimeException("Please, don't swallow me!")
  } else {
    i
  }
}.to(Sink.foreach { i =>
  println(s"Received $i")
}).run()
_

run()Unitを返すため。

15
Matthias Langer

Akk-streamsを使い始めたときも、同様の質問がありました。 Supervision.Deciderは役立ちますが、常にそうとは限りません。

残念ながら、ActionPublisherでスローされた例外はキャッチされません。処理されたようです。ActorPublisher.onErrorが呼び出されましたが、Supervision.Deciderに到達していません。ドキュメントで提供されている単純なストリームで動作します。

Sink.actorRefを使用しても、エラーはアクターに到達しません。

そして実験のために私は次のサンプルを試しました

val stream = Source(0 to 5).map(100 / _)
stream.runWith(Sink.actorSubscriber(props))

この場合、例外はDeciderによってキャッチされましたが、アクターサブスクライバーには到達しませんでした。

全体的に、一貫性のない動作だと思います。 Streamのエラーを処理するために1つのメカニズムを使用することはできません。

私の元のSO質問: Custom Supervision.DeciderはActorPublisherによって生成された例外をキャッチしません

そして、これが追跡されているakkaの問題です: https://github.com/akka/akka/issues/18359

5
expert