我在Glassfish 4上运行多租户系统(Java EE),该系统在一年中的特定时段一次接收大约500个打印作业请求。在此期间之前,资源足以处理这些请求,但在此期间,请求对服务器来说只会变得太多,这会导致大量的停机时间。我解决问题的想法是简单地为这些请求的处理方式带来一些顺序。也就是说,一种先到先服务的东西,其中一个请求被处理,然后下一个请求被处理,直到没有更多的请求。我尝试将其构建为一种服务,不断检查是否有任何请求,然后按顺序为请求提供服务。
根据我从StackOverflow找到的解决方案以及大量在线搜索,我已将其缩小为几个。但我有一些顾虑:
调度:我看到的大多数作业调度实现都需要一些重复的间隔才能执行任务。这对我的系统不起作用,因为每个打印作业的完成时间取决于生成多少页报告。它可能是5页,也可能是50.换句话说,我不知道请求服务需要多长时间。
Java消息服务(JMS):我考虑过使用JMS队列,但我只是不明白如何将它与我目前的情况联系起来。我知道它的意思是用于消息传递,也许可以解决我的一部分问题,但我还没看到。
无休止的循环:这看起来非常俗气,坦率地说,我甚至不想尝试使用Java EE应用程序和缺乏资源的系统。
我希望得到的建议是,总而言之,我将如何实现一个无休止地接收请求的系统,无论服务时间多长都能为它们服务,并继续下一个请求。如果没有任何请求,它会等待。如果请求太多,它只是按接收顺序为它们提供服务。
首先编辑: 因此,在考虑多租户和当前系统的整体复杂性之后,我决定创建另一个系统来接收客户端请求以生成这些结果。该提议的系统本身不会生成报告,而只是要求当前系统生成报告,该报告随后将通过电子邮件发送给客户。请求的排队(我认为)可以在提出的系统中实现。现在我只需要弄清楚这个系统如何也是一个Java EE应用程序。也许这就是其中一些答案发挥作用的重点。你的想法将深受赞赏。
答案 0 :(得分:0)
您可以尝试使用一个发件人和多个接收器实现消息传递的点对点模型。在这种情况下,将保留来自发送方的所有消息的队列将控制消息的消费,使得每条消息仅由一个接收方接收。
NB!在这种情况下,队列不保证接收顺序,但据我所知,这不是你的情况。
如此底线,尝试实现基于Java Messaging API的基础架构。
顺便说一句,如果它不重要,您可以设置消息的有效期并提高应用程序的稳定性。
答案 1 :(得分:0)
经过一个月的认真工作后,我找到了一个有效且已经实施的解决方案。
根据我的第一次编辑,我确实创建了一个新系统。但不是我最初想的那样。新系统能够生成我需要的报告。它的唯一职责是接收来自现有系统的请求,将它们保存在数据库中,并在适当时为这些请求生成报告。
避免使用JMS:在提出问题时,我需要JMS提供两件事,即:
(1)某种排序请求的队列,
(2)使客户端调用异步。
但是没有做太多,我能够用我的servlet实现异步调用。显然,asyncSupported
注释上有一个属性(@WebServlet
),使servlet调用异步。
@WebServlet(name = "ReportGenServlet", urlPatterns = {"/report-gen-servlet-path"}, asyncSupported = true)
接下来,通过为这500个请求提供可靠的持久存储(可能会或可能不会立即提供服务),我开始实施队列处理器,以先来先服务的方式为请求提供服务。
import javax.ejb.*;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;
/**
* Created by jaletechs on 4/23/18.
*/
/*
A Report Request Queue Processor
Checks for new and failed report requests every X minutes,
and processes the requests accordingly
*/
@Singleton
@LocalBean
@Startup
public class ReportRequestQueueProcessor {
@EJB
private SomeJobEjb ejb;
@EJB
private ReportRequestEntityMngrLocal requestEntityMngr;
private AtomicBoolean busy = new AtomicBoolean(false);
@Lock
@Schedule(second = "*/30", minute = "*", hour = "*", persistent = false)
public void atSchedule() throws InterruptedException{
processRequests();
}
@Lock(LockType.READ)
public void processRequests() throws InterruptedException{
if (!busy.compareAndSet(false, true)){
return;
}
try {
List<ReportRequest> requestList = requestEntityMngr.getPending(50);
for(ReportRequest request: requestList) {
ejb.generateAndEmailReport(request);
}
} finally {
busy.set(false);
}
}
}
我必须说我很喜欢这种方法。 @Lock
注释与我的逻辑中的AtomicBoolean
测试一起允许当前正在运行的处理任务完成而不会中断,即使容器尝试刷新也是如此。这非常适合处理没有固定完成时间的任务。此外,通过一些JPA查询,我可以获取旧的但尚未处理的请求,以及由于某种原因或其他原因而失败的请求。这种有序性大大减少了服务器停机的次数。此外,我发现客户愿意稍等一下,只要他们在合理的时间内保证结果。
我还应该补充一点,我优化了我的报告生成代码。事实上,我必须承认这应该是第一步。当我完成优化时,生成时间为92秒的报告在13秒内生成。这有很大的进步。
所以,这就是我解决问题的方法。问题,意见和建议将不胜感激。感谢您的帮助。