在使用 AKKA 的 Java 应用程序中,我想使用<与外部系统 异步进行交互< strong>基于TCP IP的JSON 。要求&amp;响应与发出请求的应用程序提供的ID 相关。外部系统来自第三方,因此我们将其视为黑盒子(只有界面定义明确)。
例如,假设我调用外部系统来检查某个人的帐户余额。请求看起来像:
{id=1234, account_name: "John Doe", question: "accountbalance"}
相应的响应将在几秒钟后(异步)到达,并且看起来像:
{id=1234, answer: "$42.87"}
一秒钟内会有数千个这样的请求。我的问题:
感谢。
答案 0 :(得分:2)
我认为可以设置Akka来处理你在这里要做的事情。我不能说达到你想要的吞吐量,因为它将依赖于其他东西(比如核心等......),但我可以为你提供一个非常高级的方法来关联请求和响应。我不会涵盖TCP部分,因为它与正确的Akka设计无关(我会将该部分留给您)
我从单个实例actor开始,可能称为QuestionMaster
,其中所有对此外部系统的请求都被路由。在这里,我将启动另一个actor的新实例,可能称为QuestionAsker
,并将该actor上的name
设置为请求的id。这将允许您查找正确的actor以在稍后返回时处理答案。然后,我会将消息从QuestionMaster
转发到新的QuestionAsker
实例。
然后QuestionAsker
实例可以执行准备TCP调用所需的任何操作,然后调用另一个处理低级TCP的Actor(可能使用Netty,其中有一个Channel作为其内部状态)。然后,QuestionAsker
应存储sender
,以便它可以响应正确的调用者,然后调用setReceiveTimeout
来处理答案未及时返回的情况。如果达到超时,我会将错误消息发送回先前存储的sender
,然后停止此QuestionAsker
实例。
当TCP actor从远程系统获得响应时,它可以将消息发送回QuestionMaster
,表明它得到了响应。该消息将包含响应的id。然后,QuestionMaster
将使用context.child(requestId)
来查找等待该响应的QuestionAsker
实例。如果它解析为actor实例,它会将该消息转发给该actor。从那里开始,QuestionAsker
可以做任何准备响应所需的操作,然后回复原始sender
,然后自行停止。
同样,这是非常高的水平,但这是使用Akka处理外部系统的请求/响应范例的一种可能方法,其中响应将异步进入并且需要与原始请求相关联。
该流的代码(不包括tcp actor)如下所示:
case class AskQuestion(id:Long, accountName:String, question:String)
case class QuestionAnswer(id:Long, answer:String)
case class QuestionTimeout(id:Long)
class QuestionMaster(tcpHandler:ActorRef) extends Actor{
def receive = {
case ask:AskQuestion =>
val asker = context.actorOf(Props(classOf[QuestionAsker], tcpHandler), ask.id.toString)
asker.forward(ask)
case answer:QuestionAnswer =>
val asker = context.child(answer.id.toString)
asker match{
case Some(ref) => ref.forward(answer)
case None =>
//handle situation where there is no actor to handle the answer
}
}
}
class QuestionAsker(tcpHandler:ActorRef) extends Actor{
import context._
import concurrent.duration._
def receive = {
case ask:AskQuestion =>
//Do whatever other prep work here if any then send to tcp actor
tcpHandler ! ask
setReceiveTimeout(5 seconds)
become(waitingForAnswer(ask, sender))
}
def waitingForAnswer(ask:AskQuestion, caller:ActorRef):Receive = {
case ReceiveTimeout =>
caller ! QuestionTimeout(ask.id)
context stop self
case answer:QuestionAnswer =>
//do any additional work to prep response and then respond
caller ! answer
context stop self
}
}