简体   繁体   English

如何在HttpServlet中处理大数据处理?

[英]How to handle large data processing in HttpServlet?

In my Java EE application, there's a servlet to handle a large data process. 在我的Java EE应用程序中,有一个servlet来处理大数据过程。 This process indexes all entities in the database (13 millions records) and it need at least 15 minutes to complete. 此过程将索引数据库中的所有实体(1300万条记录),并且至少需要15分钟才能完成。 Since the server takes too long time on processing, an TIMEOUT exception was thrown by the client. 由于服务器处理时间过长,因此客户端抛出了TIMEOUT异常。

@WebServlet("/index")
public class IndexServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    private Logger logger = Logger.getLogger(this.getClass());

    @EJB
    private AddressSession addressSession;

    protected void service(HttpServletRequest request,
            HttpServletResponse response) {

        try {
            addressSession.index();
        } catch (InterruptedException e) {
            logger.error(e);
        }
    }
}

My question is how to handle this situation ? 我的问题是如何处理这种情况? Should I use asynchronous processing ? 我应该使用异步处理吗?

For processes that take so long the web interface should be just a "job submission" that immediately acknowledges the receipt of the request and queues the job for processing. 对于花费很长时间的流程,Web界面应该仅仅是“作业提交”,它可以立即确认请求的接收并将作业排队等待处理。 Then you decouple this "by nature" back end process from your web layer. 然后,您可以将此“自然”后端过程与Web层分离。

For example, you don't use the http thread pool to do the back end processing. 例如,您不使用http线程池进行后端处理。 Have a separate Executor for processing the submitted job. 有一个单独的执行器来处理提交的作业。 You can create the executor for example with 您可以创建执行程序,例如

ExecutorService executorService = Executors.newFixedThreadPool(10);

Then when a request comes in, you submit the job like so: 然后,当有请求进入时,您可以像这样提交作业:

executorService.execute(new Runnable() {
    public void run() {
        // processing
    }
});

Now if you also want to provide information about the status of your job through some web interface you need to do some more work. 现在,如果您还想通过某些Web界面提供有关工作状态的信息,则需要做更多的工作。 You might want to assign a unique id to the submitted job and return it with the acknowledgement of the receipt. 您可能想为提交的作业分配一个唯一的ID,并在回执中将其返回。

In this case since you are using EJBs (if you're on EJB 3.1 or higher) you don't need to program the Executor by yourself. 在这种情况下,由于您正在使用EJB(如果您使用的是EJB 3.1或更高版本),则无需自己对Executor进行编程。 You can use the Asynchronous annotation on your EJB method to do this asynchronous processing declaratively. 您可以在EJB方法上使用“ 异步”注释来声明性地执行此异步处理。

Enable asynchronous support 启用异步支持

As @user2494817 said, asynchronous processing can be used in this situation. 正如@ user2494817所说,在这种情况下可以使用异步处理。 To enable asynchronous processing on a servlet, set the parameter asyncSupported to true on the @WebServlet annotation : 要在Servlet上启用异步处理,请在@WebServlet批注上将参数asyncSupported设置为true:

@WebServlet(urlPatterns = {"/index"}, asyncSupported = true)

Use asynchronous context 使用异步上下文

Then we need to use the javax.servlet.AsyncContext class, which provides the functionality that we need to perform asynchronous processing inside service methods. 然后,我们需要使用javax.servlet.AsyncContext类,该类提供在服务方法内部执行异步处理所需的功能。 To obtain an instance of AsyncContext, call the startAsync() method on the request object of the service method. 若要获取AsyncContext的实例,请在服务方法的请求对象上调用startAsync()方法。

AsyncContext asyncContext = request.startAsync();

This call puts the request into asynchronous mode and ensures that the response is not committed after exiting the service method. 此调用将请求置于异步模式,并确保退出服务方法后不提交响应。 You have to generated the response in the asynchronous context after the blocking operation completes or dispatch the request to another servlet. 阻塞操作完成后,您必须在异步上下文中生成响应,或者将请求分派到另一个Servlet。

This is described in Oracle Java EE 7 Tutorial - 17.12 Asynchronous Processing in Servlets . Oracle Java EE 7教程17.12 Servlet中的异步处理中对此进行了描述。 My solution considers only one user, for other use-cases, we might need more complex mechanism. 我的解决方案仅考虑一个用户,对于其他用例,我们可能需要更复杂的机制。


@WebServlet(urlPatterns = {"/index"}, asyncSupported = true)
public class IndexServlet extends HttpServlet {

    private static final long serialVersionUID = 1L;
    private Logger logger = Logger.getLogger(this.getClass());

    @EJB
    private AddressSession addressSession;

    protected void service(HttpServletRequest request,
            HttpServletResponse response) {

        AsyncContext asyncContext = request.startAsync();
        asyncContext.start(new Runnable() {
            @Override
            public void run() {
                try {
                    addressSession.index();
                } catch (InterruptedException e) {
                    logger.error(e);
                }
        }});
    }
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM