我已经创建了一个REST API-简而言之,我的客户访问了一个特定的URL,并且她返回了JSON响应。
在内部,单击URL时会启动一个非常复杂的过程,并且随着使用微服务体系结构,涉及各种服务。
我注意到一些性能瓶颈,因此决定切换到消息队列系统。现在的想法是,一旦用户点击了URL,请求就会在内部消息队列中发布,等待其被使用。该使用者将处理并重新发布到队列中,这将发生很多次,直到最后,为用户服务的同一节点将收到处理后的响应,并传递给用户。
现在正在使用异步“发射后遗忘”模式。但是我的问题是,为特定人员服务的节点如何在处理后的结果返回且没有阻塞的情况下记住它正在为谁服务的节点(即它可以处理多个请求直到收到响应)?如果有什么不同,我的堆栈看起来像这样:TomCat,Spring,Kubernetes和RabbitMQ。
总而言之,请求节点(其工作是将队列中的项目推送)如何与请求JSON响应的客户端(即,客户端正在等待JSON响应)保持开放的连接,并接收返回的数据。正确的客户?
答案 0 :(得分:4)
根据您对客户端的控制程度,您会遇到几种不同的情况。
如果无法更改客户端行为,则必须保持会话打开状态,直到未完全处理请求。这可以通过使用一组工作程序(将来/协程,线程或进程)来实现,其中每个工作程序都为给定请求保持会话打开。
此方法没有什么弊端,我会把它作为最后的手段。首先,您将只能处理与池大小成比例的有限数量的并发请求。最后,由于您的处理工作已排在队列后面,因此您的前端将无法估算完成任务所需的时间。这意味着您将不得不处理容易失败的持久会话(如果用户放弃,该怎么办?)。
如果可以更改客户端行为,则最常见的方法是使用完全异步的流。当客户端发起一个请求时,它被放置在队列中并返回一个任务标识符。客户端可以使用给定的TaskId
来轮询状态更新。客户端每次请求有关任务的更新时,您只需检查任务是否完成,然后做出相应的响应。任务仍在进行时,一种常见的模式是让前端在重试之前将估计的时间返回给客户端。这使您的服务器可以控制客户端轮询的频率。如果您的体系结构支持它,那么您可以加倍努力,并提供有关进度的信息。
正在进行任务时的示例响应:
{"status": "in_progress",
"retry_after_seconds": 30,
"progress": "30%"}
更复杂而优雅的解决方案是使用HTTP回调。简而言之,当客户端请求一个新任务时,它会提供一个元组(URL,方法),服务器可以使用它来表示处理已完成。然后,它等待服务器将信号发送到给定的URL。您可以看到更好的解释here。在大多数情况下,此解决方案是过大的。但是我认为值得一提。
答案 1 :(得分:3)
一种选择是使用spring提供的DeferredResult,但这意味着您需要在请求服务节点中维护一些线程池,并且最大数量不行。活动线程的数量将决定系统的吞吐量。有关如何实现DeferredResult的更多详细信息,请参见此链接https://www.baeldung.com/spring-deferred-result