Scala 如何使用ask模式和监督处理异常

Scala 如何使用ask模式和监督处理异常,scala,akka,akka-supervision,Scala,Akka,Akka Supervision,我应该如何处理DbActor在此处引发的异常?我不知道该怎么处理,该不该管这个失败的案子 class RestActor extends Actor with ActorLogging { import context.dispatcher val dbActor = context.actorOf(Props[DbActor]) implicit val timeout = Timeout(10 seconds) override val supervisorStrate

我应该如何处理DbActor在此处引发的异常?我不知道该怎么处理,该不该管这个失败的案子

class RestActor extends Actor with ActorLogging {
  import context.dispatcher

  val dbActor = context.actorOf(Props[DbActor])
  implicit val timeout = Timeout(10 seconds)


  override val supervisorStrategy: SupervisorStrategy = {
    OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
      case x: Exception => ???
    }
  }

  def receive = {
    case GetRequest(reqCtx, id) => {

        // perform db ask
       ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete {
        case Success(obj) => { // some stuff }
        case Failure(err) => err match {
          case x: Exception => ???
        }
      }
    }
  }
}

很高兴得到您的想法,提前谢谢

根据代码示例中的问题,我可以在这里看到几个问题:

  • 当我在如何处理异常的定义中覆盖默认的主管行为时,我可以做什么

  • 当使用
    ask
    时,当我在等待的
    未来
    上得到
    失败
    结果时,我可以做哪些事情

  • 让我们先从第一个问题开始(通常是个好主意)。当您重写默认的管理器策略时,您就可以更改如何处理子参与者中的某些类型的未处理异常,以了解如何处理失败的子参与者。前一句话中的关键词是
    未处理
    。对于执行请求/响应的参与者,您实际上可能希望处理(捕获)特定的异常并返回某些响应类型(或者在将来的上游失败,稍后将详细介绍),而不是让它们不经处理。当发生未处理的异常时,您基本上失去了用问题描述响应发送方的能力,发送方可能会得到一个
    TimeoutException
    ,因为他们的
    未来
    将永远无法完成。一旦确定了显式处理的内容,就可以在定义自定义监视器策略时考虑所有其他异常。在这个街区内:

    OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
      case x: Exception => ???
    }
    
    您有机会将异常类型映射到故障
    指令
    ,该指令从监督的角度定义了如何处理故障。这些选择包括:

  • 停止-完全停止子参与者,不再向其发送任何消息

  • Resume-恢复失败的子级,而不是重新启动它,从而保持其当前内部状态

  • 重启-与resume类似,但在这种情况下,旧实例被丢弃,新实例被构造,内部状态被重置(预启动)

  • 上报-向上级主管的上级上报整个链

  • 因此,假设给定一个
    SQLException
    您想要恢复,并且给定所有其他您想要重新启动的代码,那么您的代码将如下所示:

    OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
      case x: SQLException => Resume
      case other => Restart
    }
    
       ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete {
        case Success(obj) => reqCtx.complete(200, "All good!")
        case Failure(err:TimeoutException) => reqCtx.complete(500, "Request timed out")
        case Failure(ex) => reqCtx.complete(500, ex.getMessage)
      }
    
       val origin = sender
       ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete {
        case Success(obj) => origin ! someResponseObject
        case Failure(ex) => origin ! Status.Failure(ex)
      }
    
    现在来看第二个问题,当
    未来
    本身返回
    失败
    响应时,该怎么办。在这种情况下,我想这取决于
    未来的结果是什么。如果rest参与者本身负责完成http请求(假设httpCtx上有一个
    complete(statusCode:Int,message:String)
    函数),那么您可以执行以下操作:

    OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
      case x: SQLException => Resume
      case other => Restart
    }
    
       ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete {
        case Success(obj) => reqCtx.complete(200, "All good!")
        case Failure(err:TimeoutException) => reqCtx.complete(500, "Request timed out")
        case Failure(ex) => reqCtx.complete(500, ex.getMessage)
      }
    
       val origin = sender
       ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete {
        case Success(obj) => origin ! someResponseObject
        case Failure(ex) => origin ! Status.Failure(ex)
      }
    
    现在,如果另一个参与者上游负责完成http请求,并且您需要响应该参与者,那么您可以执行以下操作:

    OneForOneStrategy(maxNrOfRetries = 10, withinTimeRange = 10 seconds) {
      case x: SQLException => Resume
      case other => Restart
    }
    
       ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete {
        case Success(obj) => reqCtx.complete(200, "All good!")
        case Failure(err:TimeoutException) => reqCtx.complete(500, "Request timed out")
        case Failure(ex) => reqCtx.complete(500, ex.getMessage)
      }
    
       val origin = sender
       ask(dbActor, ReadCommand(reqCtx, id)).mapTo[SomeObject] onComplete {
        case Success(obj) => origin ! someResponseObject
        case Failure(ex) => origin ! Status.Failure(ex)
      }
    
    这种方法假设在成功块中,您首先希望在响应之前对结果对象进行按摩。如果您不想这样做,并且希望将结果处理推迟到发送方,则可以执行以下操作:

       val origin = sender
       val fut = ask(dbActor, ReadCommand(reqCtx, id))
       fut pipeTo origin
    

    对于较简单的系统,可能需要捕获并转发所有错误。为此,我创建了一个小函数来包装receive方法,而不必担心监督:

      import akka.actor.Actor.Receive
      import akka.actor.ActorContext
      /**
       * Meant for wrapping the receive method with try/catch.
       * A failed try will result in a reply to sender with the exception.
       * @example
       *          def receive:Receive = honestly {
       *            case msg => sender ! riskyCalculation(msg)
       *          }
       *          ...
       *          (honestActor ? "some message") onComplete {
       *            case e:Throwable => ...process error
       *            case r:_ => ...process result
       *          }
       * @param receive
       * @return Actor.Receive
       *
       * @author Bijou Trouvaille
       */
        def honestly(receive: =>Receive)(implicit context: ActorContext):Receive = { case msg =>
          try receive(msg) catch { case error:Throwable => context.sender ! error }
        }
    

    然后,您可以将其放入包文件并导入la
    akka.pattern.pipe
    等。显然,这不会处理异步代码引发的异常。

    我想我明白了,这里真正的关键是理解什么是未处理的