Java 参与者模型与外部系统的异步通信
在我的Java应用程序中使用AKKA,我希望通过TCP-IP使用JSON与外部系统进行异步交互。请求和响应通过发出请求的应用程序提供的ID进行关联。外部系统来自第三方,因此我们将其视为黑盒(只有接口定义良好) 例如,假设我调用外部系统来检查某个人的帐户余额。请求将类似于:Java 参与者模型与外部系统的异步通信,java,asynchronous,akka,actor,Java,Asynchronous,Akka,Actor,在我的Java应用程序中使用AKKA,我希望通过TCP-IP使用JSON与外部系统进行异步交互。请求和响应通过发出请求的应用程序提供的ID进行关联。外部系统来自第三方,因此我们将其视为黑盒(只有接口定义良好) 例如,假设我调用外部系统来检查某个人的帐户余额。请求将类似于: {id=1234, account_name: "John Doe", question: "accountbalance"} 相应的响应将在几秒钟后(异步)到达,如下所示: {id=1234, answer: "$42.8
{id=1234, account_name: "John Doe", question: "accountbalance"}
相应的响应将在几秒钟后(异步)到达,如下所示:
{id=1234, answer: "$42.87"}
每秒将有数千个此类请求。我的问题是:
谢谢。我认为Akka可以被设置来处理您在这里试图做的事情。我不能说要达到您所期望的吞吐量,因为这将取决于其他因素(如内核等),但我可以为您提供一种非常高级别的方法来关联请求和响应。我不会涉及TCP部分,因为这与正确的Akka设计无关(我将把这部分留给您) 我将从一个单实例参与者开始,可能称为
QuestionMaster
,其中所有对该外部系统的请求都将被路由。在这里,我将启动另一个参与者的新实例,可能称为QuestionAsker
,并将该参与者的名称设置为请求的id。这将允许您查找正确的参与者来处理稍后返回的答案。然后,我会将消息从QuestionMaster
转发到新的QuestionAsker
实例
然后,QuestionAsker
实例可以做它需要做的任何事情来准备TCP调用,然后调用另一个处理低级TCP的参与者(可能使用Netty,其中它有一个通道作为它的内部状态)。然后,QuestionAsker
应该存储发送者
,这样它就可以响应正确的呼叫者,然后调用setReceiveTimeout
来处理答案没有及时返回的情况。如果达到超时,我会将错误消息发送回先前存储的发送者
,然后停止此提问者
实例
当TCP参与者从远程系统获得响应时,它可以向QuestionMaster
发回一条消息,表明它得到了响应。该消息将包含响应的id。然后,QuestionMaster
将使用context.child(requestId)
查找等待该响应的QuestionAsker
实例。如果它解析为一个actor实例,它将把该消息转发给该actor。从那里,QuestionAsker
可以做任何它需要做的事情来准备回复,然后回复原始的发送者
,然后停止自己
同样,这是一个非常高的级别,但这是使用Akka处理外部系统的请求/响应范例的一种可能的方法,在外部系统中,响应将异步进入,并且需要与原始请求关联
该流(不包括tcp参与者)的代码如下所示:
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
}
}