在REST服务之前拥有请求队列的最佳技术解决方案(框架/方法)是什么?这样我就可以增加REST服务实例的数量以获得更高的可用性,并将Request队列放在前面以形成服务客户端的服务/事务边界.
Wil*_*ung 10
根据您的目标,这里有几个问题.
首先,它只会提高后端资源的可用性.考虑是否有5台服务器在后端处理队列请求.如果其中一个服务器出现故障,则排队的请求应回退到队列中,并重新传送到其余4个服务器中的一个.
但是,当这些后端服务器正在处理时,前端服务器仍然保持实际的启动请求.如果其中一个前端服务器出现故障,那么这些连接将完全丢失,并且由原始客户端重新提交请求.
前提可能是更简单的前端系统的故障风险更低,而且对于与软件相关的故障也是如此.但网络卡,电源,硬盘驱动器等对于人类的这种虚假希望和对所有人的平等惩罚是非常不可知的.因此,在谈论整体可用性时请考虑这一点.
至于设计,后端是一个等待JMS消息队列的简单过程,并在它们到来时处理每个消息.有很多可用的示例,任何JMS服务器都适合高级别.您所需要的只是确保消息处理是事务性的,这样如果消息处理失败,消息将保留在队列中并可以重新传递给另一个消息处理程序.
您的JMS队列的主要要求是可群集的.JMS服务器本身是系统中的单点故障.丢失了JMS服务器,您的系统几乎已经死了,因此您需要能够集中服务器并让消费者和生产者适当地处理故障转移.同样,这是JMS服务器特定的,大部分是这样做的,但它在JMS世界中非常常见.
前端是事情变得有点棘手的地方,因为前端服务器是从REST请求的同步世界到后端处理器的异步世界的桥梁.REST请求遵循通常的RPC模式,即从套接字消耗请求有效负载,保持连接打开,处理结果,并将结果传回原始套接字.
为了表明这一点,你应该看看处理Servlet 3.0的异步Servlet,它可以在Tomcat 7,最新的Jetty(不确定是什么版本),Glassfish 3.x和其他版本中使用.
在这种情况下,您要做的是当请求到达时,您将名义上同步的Servlet调用转换为异步调用HttpServletRequest.startAsync(HttpServletRequest request, HttpServletResponse response)
.
这将返回一个AsynchronousContext,一旦启动,就允许服务器释放处理线程.然后你做几件事.
此时,初始处理完成,您只需从doGet(或服务,或其他)返回.由于您尚未调用AsyncContext.complete(),因此服务器不会关闭与服务器的连接.由于您通过ID在地图中拥有AsyncContext存储,因此暂时可以安全地保存.
现在,当您将请求提交给JMS队列时,它包含:请求的ID(您生成的),请求的任何参数以及发出请求的实际服务器的标识.最后一点很重要,因为处理结果需要返回原点.原点由请求ID和服务器ID标识.
当您的前端服务器启动时,它还启动了一个线程,它的工作就是监听JMS响应队列.当它设置其JMS连接时,它可以设置一个过滤器,例如"仅为ABC123的ServerID提供消息".或者,您可以为每个前端服务器创建一个唯一的队列,后端服务器使用服务器ID来确定要返回答复的队列.
当后端处理器使用消息时,它们会获取请求ID和参数,执行工作,然后获取结果并将它们放到JMS响应队列中.当它返回结果时,它会将原始ServerID和原始请求ID添加为消息的属性.
因此,如果您最初收到前端服务器ABC123的请求,后端处理器将把结果发送回该服务器.然后,当收到消息时,将通知该侦听器线程.侦听器线程任务是获取该消息并将其放到前端服务器内的内部队列中.
此内部队列由线程池支持,该线程池的作用是将请求有效负载发送回原始连接.它通过从消息中提取原始请求ID,从前面讨论的内部映射查找AsyncContext,然后将结果发送到与AsyncContext关联的HttpServletResponse来完成此操作.最后,它调用AsyncContext.complete()(或类似方法)来告诉服务器您已完成并允许它释放连接.
对于内务处理,您应该在前端服务器上有另一个线程,它的工作是检测请求在地图中等待的时间太长.部分原始邮件应该是请求开始的时间.这个线程可以每秒唤醒一次,扫描地图中的请求,对于任何已经存在太长时间(例如30秒)的线程,它可以将请求放到另一个内部队列中,由一组处理程序使用,这些处理程序旨在通知请求超时的客户端.
您需要这些内部队列,以便主处理逻辑不会在客户端等待消耗数据.它可能是一个缓慢的连接或其他东西,所以你不想阻止所有其他待处理的请求逐个处理它们.
最后,您需要考虑到,您可能会从响应队列中获取内部地图中不再存在的请求的消息.例如,请求可能已超时,因此不应该再存在.另一方面,该前端服务器可能已经停止并重新启动,因此挂起请求的内部映射将只是空的.此时,如果您检测到您对不再存在的请求有回复,则应该简单地丢弃它(好吧,记录它,然后丢弃它).
您无法重用这些请求,负载均衡器不会回到客户端.如果客户端允许您通过已发布的端点进行回调,那么,确保您可以让另一个JMS消息处理程序发出这些请求.但这不是REST类的东西,在这个级别的讨论中的REST更多是客户端/服务器/ RPC.
至于哪个框架支持比原始Servlet更高级别的异步Servlet(例如Jersey for JAX-RS或类似的东西),我不能说.我不知道在那个级别支持它的框架是什么.看起来这是泽西2.0的一个功能,尚未推出.可能还有其他人,你必须环顾四周.另外,不要注意Servlet 3.0.Servlet 3.0只是个别容器中使用的技术的标准化一段时间(特别是Jetty),因此您可能希望查看Servlet 3.0之外的容器特定选项.
但概念是一样的.最重要的是具有过滤的JMS连接的响应队列侦听器,到AsyncContext的内部请求映射,以及用于在应用程序中执行实际工作的内部队列和线程池.