我使用ask模式在名为fun-F的函数中向一个名为actor-A的actor发送请求。 actor会以异步方式获取另一个系统生成的ID,当这个完成时,我会将包含此ID的消息转发给另一个名为actor-B的actor,actor-B将执行一些DB操作然后发送回数据库操作导致向发件人发送消息,因为在我的情况下我使用正向模式,所以actor-B将发件人识别为fun-F,akka会给fun-F一个临时的actor名称,所以返回的值应该是被送到临时演员。
我的问题是:
如果我使用sync-method从另一个系统获取ID,然后将此消息转发给actor-B,在actor-B的DB操作之后,结果可以传递给fun-F的值,并且有趣-F被akka框架运行时定义为临时actor Actor [akka:// ai-feedback-service / temp / $ b]。
如果我使用async-method从另一个系统获取ID,当它完成时,我将在另一个回调线程中的oncompleted {}代码块中转发消息,成功处理actor-B中的DB操作,但是返回的值不能传递给fun-F中定义的值,在这种情况下,fun-F被akka框架运行时定义为Actor [akka:// ai-feedback-service / deadLetters]。所以actor-B迷失了方向,不知道如何返回或者这个消息应该传递到哪里,这将导致我的日志中出现一个Ask time out exception抛出。
我该如何处理这个问题?或者我怎样才能避免这封死信要求超时?
以下是我的代码:
// 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")
}
答案 0 :(得分:0)
要避免死信问题,请执行以下操作:
对于每个请求,请使用您可以与请求的最终目标关联的标识符(可能是requestId
)。也就是说,将您传递给requestId
方法的createFeedback
绑定到该方法的调用方(ActorRef
),然后将此ID传递到您的消息链中。您可以使用地图来保存这些关联。
CreateFeedbackResponseSignal(issueId)
以包含requestId
课程中的Ticket
:CreateFeedbackResponseSignal(requestId, issueId)
。在处理演员内部Future
的异步结果时,pipe
Future
到self
的结果,而不是使用回调。
createIssue
的结果将在结果可用时发送至jiraActor
。 jiraActor
然后将结果发送到dbActor
。jiraActor
将是sender
中的dbActor
。当jiraActor
收到dbActor
的结果时,jiraActor
可以在其内部地图中查找对目标的引用。下面是一个模仿您的用例并可在ScalaFiddle中运行的简单示例:
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")
}
在ScalaFiddle中运行上面的代码会产生以下输出:
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)