Scala 当参与者包含异步方法时,这将导致带有Ask超时异常的死信错误
我使用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迷失了方向,不知道如何返回,也不知道该在何处传递此消息,这将导致在我的日志中抛出一个请求超时异常 如何处理此问题?或者如何避免此死信请求超时异常 下面是我的代码: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,
// 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)