如何处理Akka Actor

时间:2017-07-19 20:02:36

标签: scala web-services playframework timeout akka

我有以下actor向WebService发送请求:

class VigiaActor extends akka.actor.Actor {
  val log = Logging(context.system, this)

  context.setReceiveTimeout(5 seconds)

  import VigiaActor._
  def receive = {
    case ObraExists(numero: String, unidadeGestora: String) =>
      WS.url(baseURL + s"""/obras/exists/$unidadeGestora/$numero""").withHeaders("Authorization" -> newToken).get.pipeTo(sender)
    case ReceiveTimeout =>
      val e = TimeOutException("VIGIA: Receive timed out")
      throw e
  }

  override val supervisorStrategy =
    OneForOneStrategy(maxNrOfRetries = 2, withinTimeRange = 1 minute) {
      case _: ArithmeticException      => Resume
      case _: NullPointerException     => Restart
      case _: IllegalArgumentException => Stop
      case _: TimeOutException         => Resume       
      case _: Exception                => Restart
    }
}

对此actor的调用是验证方法的一部分,该方法在尝试与WS通信时应该在超时的情况下抛出异常:

implicit val timeout = Timeout(5 seconds)
lazy val vigiaActor : ActorRef = Akka.system.actorOf(Props[VigiaActor])

(vigiaActor ? VigiaActor.ObraExists(empenho.obra.get, empenho.unidadeGestora)).map {
  case r : WSResponse =>
    val exists = r.body.toBoolean

    if (!exists && empenho.tipoMeta.get.equals(4)) {
      erros.adicionarErro(controle.codigoArquivo, row, line, s"Nº de Obra não informado ou inválido para o Tipo de Meta 4 - Obras" , TipoErroImportacaoEnum.WARNING)
    }

  case _ => erros.adicionarErro(controle.codigoArquivo, row, line, s"Nº de Obra não informado ou inválido para o Tipo de Meta 4 - Obras" , TipoErroImportacaoEnum.WARNING)
}

我是这个Actor的新手,我正在尝试解决代码上的一些阻塞情况。

问题是我不知道如何捕捉"在actor调用 TimeOutException

更新

将验证方法转换为:

protected def validateRow(row: Int, line: String, empenho: Empenho, calendarDataEnvioArquivo: Calendar)(implicit s: Session, controle: ControleArquivo, erros:ImportacaoException): Unit = {
    implicit val timeout = Timeout(5 seconds)
    lazy val vigiaActor : ActorRef = Akka.system.actorOf(Props[VigiaActor])

    (vigiaActor ? VigiaActor.ObraExists(empenho.obra.get, empenho.unidadeGestora)).map {
      case e: TimeOutException => println("TIMOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOOUT!!!!")
      case r: WSResponse => {...}
    }
}

和演员ReceiveTimout部分:

case ReceiveTimeout =>
  val e = TimeOutException("VIGIA: Receive timed out")
  sender ! e

我收到以前的日志消息:

  

[INFO] [07/20/2017 10:28:05.738] [application-akka.actor.default-dispatcher-5] [akka:// application / deadLetters]来自Actor的消息[model.exception.TimeOutException] [akka:// application / user / $ c#1834419855]对于演员[akka:// application / deadLetters]未送达。 [1]遇到死信。可以使用配置设置关闭或调整此日志记录' akka.log-dead-letters'和' akka.log-dead-letters-during-shutdown'。

2 个答案:

答案 0 :(得分:1)

context.setReceiveTimeout(5 seconds)会触发向ReceiveTimeout发送VigiaActor消息,如果该演员没有收到消息五秒钟。 Akka在内部将ReceiveTimeout发送给您的演员,这就是为什么在您更新的代码中,尝试将例外发送到sender并不能达到预期效果。换句话说,sender子句中的case ReceiveTimeout =>不是ObraExists消息的原始发件人。

VigiaActor中设置接收超时与WS请求超时无关,因为如果请求超时,则不会向VigiaActor发送消息。即使在WS请求未在五秒钟内完成时向该actor发送了一条消息,同时另一条ObraExists消息可能已在该actor的邮箱中排队,因此无法触发ReceiveTimeout

简而言之,设置actor的接收超时不是处理WS请求超时的正确机制。 (使用您当前的方法将get请求的结果传递给发件人,您可以调整发件人以处理超时。事实上,我完全放弃VigiaActor并简单地制作直接在WS方法中调用validateRow,但摆脱演员可能不是你问题的重点。)

如果您必须在actor中处理WS请求超时,则执行此操作的一种方法如下:

import scala.util.{Failure, Success}

class VigiaActor extends akka.actor.Actor {
  import VigiaActor._
  val log = Logging(context.system, this)

  def receive = {
    case ObraExists(numero: String, unidadeGestora: String) =>
      val s = sender // capture the original sender
      WS.url(baseURL + s"""/obras/exists/$unidadeGestora/$numero""")
        .withHeaders("Authorization" -> newToken)
        .withRequestTimeout(5 seconds) // set the timeout
        .get
        .onComplete {
          case Success(resp) =>
            s ! resp
          case Failure(e: scala.concurrent.TimeoutException) =>
            s ! TimeOutException("VIGIA: Receive timed out")
          case Failure(_) =>
            // do something in the case of non-timeout failures
        }
  }
}

答案 1 :(得分:0)

我认为你过分诠释了“让它崩溃”的心态。在异常情况下,您只在Actors 中抛出异常。也就是说,如果出现意外崩溃的情况,你可以构建你的Actors来应对。但如果它是正常和合理预期的东西,你就像对待任何其他代码路径一样对待它。

所以在你的情况下,它与throw或catch无关 - 在你的ReceiveTimeout子句中,只是将消息发送回原始发件人,说请求因超时而失败,并让发件人处理它,但他们认为合适。它与你的成功案例非常相似。