Java 在servlet环境中处理批处理作业的线程

Java 在servlet环境中处理批处理作业的线程,java,servlets,spring-mvc,threadpool,batch-processing,Java,Servlets,Spring Mvc,Threadpool,Batch Processing,我有一个SpringMVC,Hibernate,(Postgres9DB)Web应用程序。管理员用户可以发送一个请求来处理近200000条记录(每条记录都是通过联接从各个表收集的)。每周或每月(或当数据达到约200000/100000条记录的限制时)都会要求进行此类操作。在数据库端,我正确地实现了批处理 问题:这样一个长时间运行的请求会阻塞服务器线程,这会导致普通用户受到影响 要求:此请求的高响应时间不是问题。所需要的是不要让其他用户因为这个耗时的过程而受苦 我的解决方案: 使用Sprin

我有一个SpringMVC,Hibernate,(Postgres9DB)Web应用程序。管理员用户可以发送一个请求来处理近200000条记录(每条记录都是通过联接从各个表收集的)。每周或每月(或当数据达到约200000/100000条记录的限制时)都会要求进行此类操作。在数据库端,我正确地实现了批处理

  • 问题:这样一个长时间运行的请求会阻塞服务器线程,这会导致普通用户受到影响

  • 要求:此请求的高响应时间不是问题。所需要的是不要让其他用户因为这个耗时的过程而受苦

  • 我的解决方案:

    使用Spring taskExecutor抽象实现线程池。因此,我可以用5或6个线程初始化线程池,并将200000条记录分成更小的块,比如每个块大小为1000条。我可以在这些区块中排队。为了进一步让普通用户能够更快地访问数据库,也许我可以让每个可运行线程睡眠2或3秒。 我看到这种方法的优点是:与一次性执行一个巨大的db交互请求不同,我们有一个跨越更长时间的异步设计。因此,其行为类似于多个普通用户请求

请一些有经验的人对此发表意见好吗? 我还读过关于使用面向消息的中间件(如JMS/AMQP或Quartz Scheduling)实现相同beahviour的内容。但坦率地说,我认为他们内部也会做同样的事情,即创建线程池并在作业中排队。那么,为什么不使用Spring taskexecutors,而不是在我的web应用程序中为这个功能添加一个全新的基础设施呢

请分享您对此的看法,并让我知道是否有其他更好的方法来做到这一点?
再次强调:完全处理所有记录的时间无关紧要,所需的是在此期间访问web应用程序的普通用户不应受到任何影响。

您可以并行处理任务,并等待所有任务完成后再返回呼叫。为此,您希望使用Java标准中自5.0以来提供的

简而言之,您可以使用容器的服务定位器来创建ExecutorCompletionService的实例

ExecutorCompletionService<List<MyResult>> queue = new ExecutorCompletionService<List<MyResult>>(executor);

// do this in a loop
queue.submit(aCallable);

//after looping 
queue.take().get(); //take will block till all threads finish
ExecutorCompletionService队列=新的ExecutorCompletionService(executor);
//循环执行此操作
排队。提交(不可更改);
//循环后
queue.take().get()//take将阻塞直至所有螺纹完成
如果您不想等待,那么可以在后台处理作业,而不阻塞当前线程,但是需要某种机制在作业完成时通知客户端。这可以通过JMS实现,或者如果您有一个ajax客户端,它可以轮询更新

Quartz也有一个作业调度机制,但Java提供了一种标准方法

编辑: 我可能误解了这个问题。如果您不希望更快的响应,而是希望限制CPU,请使用此方法

您可以创建一个类似于PollingThread的内部类,其中包含每个作业的java.util.UUID和PollingThread数量的批在外部类中定义。这将永远持续下去,并且可以调整以使您的CPU能够自由处理其他请求

 class PollingThread implements Runnable {
            @SuppressWarnings("unchecked")
            public void run(){
                Thread.currentThread().setName("MyPollingThread");
                while (!Thread.interrupted()) {
                    try {
                        synchronized (incomingList) {
                            if (incomingList.size() == 0) {
                                // incoming is empty, wait for some time
                            } else {
                                //clear the original
                                list = (LinkedHashSet<UUID>) 
                                        incomingList.clone();
                                incomingList.clear();
                            }
                        }

                        if (list != null && list.size() > 0) {
                            processJobs(list);
                        }
                        // Sleep for some time
                        try {
                            Thread.sleep(seconds * 1000);
                        } catch (InterruptedException e) {
                            //ignore
                        }
                    } catch (Throwable e) {
                        //ignore                    
                    }
                }
           }
    }
类PollingThread实现可运行{
@抑制警告(“未选中”)
公开募捐{
Thread.currentThread().setName(“MyPollingThread”);
而(!Thread.interrupted()){
试一试{
已同步(输入列表){
if(incomingList.size()==0){
//传入为空,请等待一段时间
}否则{
//清除原始文件
列表=(LinkedHashSet)
incomingList.clone();
incomingList.clear();
}
}
if(list!=null&&list.size()>0){
处理工作(名单);
}
//睡一会儿
试一试{
线程睡眠(秒*1000);
}捕捉(中断异常e){
//忽略
}
}捕获(可丢弃的e){
//忽略
}
}
}
}

您可以将任务并行化,等待所有任务完成后再返回呼叫。为此,您希望使用Java标准中自5.0以来提供的

简而言之,您可以使用容器的服务定位器来创建ExecutorCompletionService的实例

ExecutorCompletionService<List<MyResult>> queue = new ExecutorCompletionService<List<MyResult>>(executor);

// do this in a loop
queue.submit(aCallable);

//after looping 
queue.take().get(); //take will block till all threads finish
ExecutorCompletionService队列=新的ExecutorCompletionService(executor);
//循环执行此操作
排队。提交(不可更改);
//循环后
queue.take().get()//take将阻塞直至所有螺纹完成
如果您不想等待,那么可以在后台处理作业,而不阻塞当前线程,但是需要某种机制在作业完成时通知客户端。这可以通过JMS实现,或者如果您有一个ajax客户端,它可以轮询更新

Quartz也有一个作业调度机制,但Java提供了一种标准方法

编辑: 我可能误解了这个问题。如果您不希望更快的响应,而是希望限制CPU,请使用此方法

您可以创建一个类似于PollingThread的内部类,其中包含每个作业的java.util.UUID和PollingThread数量的批在外部类中定义。这将永远持续下去,并且可以调整以使您的CPU能够自由处理其他请求

 class PollingThread implements Runnable {
            @SuppressWarnings("unchecked")
            public void run(){
                Thread.currentThread().setName("MyPollingThread");
                while (!Thread.interrupted()) {
                    try {
                        synchronized (incomingList) {
                            if (incomingList.size() == 0) {
                                // incoming is empty, wait for some time
                            } else {
                                //clear the original
                                list = (LinkedHashSet<UUID>) 
                                        incomingList.clone();
                                incomingList.clear();
                            }
                        }

                        if (list != null && list.size() > 0) {
                            processJobs(list);
                        }
                        // Sleep for some time
                        try {
                            Thread.sleep(seconds * 1000);
                        } catch (InterruptedException e) {
                            //ignore
                        }
                    } catch (Throwable e) {
                        //ignore                    
                    }
                }
           }
    }
类轮询