Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/scala/16.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/backbone.js/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Scala 当参与者包含异步方法时,这将导致带有Ask超时异常的死信错误_Scala_Asynchronous_Akka_Actor - Fatal编程技术网

Scala 当参与者包含异步方法时,这将导致带有Ask超时异常的死信错误

Scala 当参与者包含异步方法时,这将导致带有Ask超时异常的死信错误,scala,asynchronous,akka,actor,Scala,Asynchronous,Akka,Actor,我使用ask模式向名为fun-F的函数中名为actor-a的参与者发送请求。参与者将以异步方式获得另一个系统生成的ID,完成后,我将向名为actor-B的另一个参与者转发包含此ID的消息,actor-B将执行一些DB操作,然后将DB操作结果以消息的形式发送回发送方,因为在我的例子中,我使用forward模式,因此actor-B将发送方识别为fun-F,akka将给fun-F一个临时actor名称,因此返回的值应传递给temp actor 我的问题是: 如果我使用sync方法从另一个系统获取ID,

我使用ask模式向名为fun-F的函数中名为actor-a的参与者发送请求。参与者将以异步方式获得另一个系统生成的ID,完成后,我将向名为actor-B的另一个参与者转发包含此ID的消息,actor-B将执行一些DB操作,然后将DB操作结果以消息的形式发送回发送方,因为在我的例子中,我使用forward模式,因此actor-B将发送方识别为fun-F,akka将给fun-F一个临时actor名称,因此返回的值应传递给temp actor

我的问题是:

如果我使用sync方法从另一个系统获取ID,然后将此消息转发给actor-B,在actor-B的DB操作之后,结果可以传递到fun-F的值,并且fun-F被定义为临时actor-actor[akka://ai-feedback-service/temp/$b]由akka框架运行时提供

如果我使用async方法从另一个系统获取ID,当它完成时,我将在另一个回调线程中的oncompleted{}代码块中转发消息,actor-B中的DB操作成功处理,但返回的值无法传递到fun-F中定义的值,在这种情况下,fun-F被定义为actor[akka://ai-feedback-service/deadLetters]因此,actor-B迷失了方向,不知道如何返回,也不知道该在何处传递此消息,这将导致在我的日志中抛出一个请求超时异常

如何处理此问题?或者如何避免此死信请求超时异常

下面是我的代码:

// this is the so-called fun-F [createFeedback]
def createFeedback(query: String, 
                   response: String, 
                   userId: Long, 
                   userAgent: String, 
                   requestId: Long, 
                   errType: Short, 
                   memo: String): Future[java.lang.Long] = {
    val ticket = Ticket(userId,
                        requestId,
                        query,
                        response,
                        errType,
                        userAgent,
                        memo)
    val issueId = (jiraActor ? CreateJiraTicketSignal(ticket))
                  .mapTo[CreateFeedbackResponseSignal].map{ r =>
        r.issueId.asInstanceOf[java.lang.Long]
    }
    issueId
}


//this is the so-called actor-A [jiraActor]
//receive method are run in its parent actor for some authorization
//in this actor only override the handleActorMsg method to deal msg
override def handleActorMsg(msg: ActorMsgSignal): Unit = {
    msg match {
        case s:CreateJiraTicketSignal =>
            val issueId = createIssue(cookieCache.cookieContext.flag,
                                     cookieCache.cookieContext.cookie,
                                     s.ticket)
            println(s">> ${sender()} before map $issueId")
            issueId.map{
                case(id:Long) =>
                    println(s">> again++issueId = $id ${id.getClass}")
                    println(s">>> $self / ${sender()}")
                    println("again ++ jira action finished")
                    dbActor.forward(CreateFeedbackSignal(id,s.ticket))
                case(message:String) if(!s.retry) =>
                    self ! CreateJiraTicketSignal(s.ticket,true)
                case(message:String) if(s.retry) =>
                    log.error("cannot create ticket :" + message)
            }
            println(s">> after map $issueId")
}


//this is the so-called actor-B [dbActor]
override def receive: Receive = {
    case CreateFeedbackSignal(issueId:Long, ticket:Ticket) =>
        val timestampTicks = System.currentTimeMillis()
        val description: String = Json.obj("question" -> ticket.query, 
                                          "answer" -> ticket.response)
                                          .toString()
        dao.createFeedback(issueId,
                           ticket.usrId.toString,
                           description,
                           FeedbackStatus.Open.getValue
                                .asInstanceOf[Byte],
                           new Timestamp(timestampTicks),
                           new Timestamp(timestampTicks),
                           ticket.usrAgent,
                           ticket.errType,
                           ticket.memo)

        println(s">> sender = ${sender()}")
        sender() ! (CreateFeedbackResponseSignal(issueId))
        println("db issue id is " + issueId)
        println("db action finished")
}

要避免死信问题,请执行以下操作:

  • 对于每个请求,使用一个标识符(可能是
    requestId
    ),您可以将该标识符与请求的最终目标相关联。也就是说,将传递给
    createFeedback
    方法的
    requestId
    绑定到调用方(
    ActorRef
    )然后通过消息传递链传递此id。您可以使用映射来保存这些关联

    • 更改
      CreateFeedbackResponseSignal(issueId)
      以包含来自
      Ticket
      类的
      CreateFeedbackResponseSignal(requestId,issueId)
  • 当从参与者内部处理
    未来
    的异步结果时,将
    未来
    的结果转换为
    自身
    ,而不是使用回调

    • 使用这种方法,当结果可用时,
      createIssue
      的结果将发送到
      jiraActor
      jiraActor
      然后将结果发送到
      dbActor
    • jiraActor
      将是
      dbActor
      中的
      发送方
      。当
      jiraActor
      dbActor
      接收结果时,
      jiraActor
      可以在其内部映射中查找对目标的引用
  • 下面是一个模拟您的用例并可在中运行的简单示例:


    嗨,Chunjef,非常感谢你的帮助,它对我很有用。顺便问一下,我还有一个问题,这样我需要将目标ActorRef存储到地图中,akka是否有其他机制不需要存储它们并以更直接的方式进行处理??
    import akka.actor._
    import akka.pattern.{ask, pipe}
    import akka.util.Timeout
    
    import language.postfixOps
    
    import scala.concurrent._
    import scala.concurrent.duration._
    
    case class Signal(requestId: Long)
    case class ResponseSignal(requestId: Long, issueId: Long)
    
    object ActorA {
      def props(actorB: ActorRef) = Props(new ActorA(actorB))
    }
    
    class ActorA(dbActor: ActorRef) extends Actor {
      import context.dispatcher
    
      var targets: Map[Long, ActorRef] = Map.empty
    
      def receive = {
        case Signal(requestId) =>
          val s = sender
          targets = targets + (requestId -> s)
          createIssue(requestId).mapTo[Tuple2[Long, Long]].pipeTo(self) // <-- use pipeTo
        case ids: Tuple2[Long, Long] =>
          println(s"Sending $ids to dbActor")
          dbActor ! ids
        case r: ResponseSignal =>
          println(s"Received from dbActor: $r")
          val target = targets.get(r.requestId)
          println(s"In actorA, sending to: $target")
          target.foreach(_ ! r)
          targets = targets - r.requestId
      }
    }
    
    class DbActor extends Actor {
      def receive = {
        case (requestId: Long, issueId: Long) =>
          val response = ResponseSignal(requestId, issueId)
          println(s"In dbActor, sending $response to $sender")
          sender ! response
      }
    }
    
    val system = ActorSystem("jiratest")
    implicit val ec = system.dispatcher
    
    val dbActor = system.actorOf(Props[DbActor])
    val jiraActor = system.actorOf(Props(new ActorA(dbActor)))
    
    val requestId = 2L
    
    def createIssue(requestId: Long): Future[(Long, Long)] = {
      println(s"Creating an issue ID for requestId[$requestId]")
      Future((requestId, 99L))
    }
    
    def createFeedback(): Future[Long] = {
      implicit val timeout = Timeout(5.seconds)
      val res = (jiraActor ? Signal(requestId)).mapTo[ResponseSignal]
      res.map(_.issueId)
    }
    
    createFeedback().onComplete { x =>
      println(s"Done: $x")
    }
    
    Creating an issue ID for requestId[2]
    Sending (2,99) to dbActor
    In dbActor, sending ResponseSignal(2,99) to Actor[akka://jiratest/user/$b#-710097339]
    Received from dbActor: ResponseSignal(2,99)
    In actorA, sending to: Some(Actor[akka://jiratest/temp/$a])
    Done: Success(99)