在异步Servlet中将响应传递给客户端?

时间:2013-12-27 13:23:48

标签: java multithreading servlets asynchronous

我在理解异步servlet如何工作方面遇到了问题,一般来说,servlet如何将响应传递给客户端 我想要做的是通过ajax将视频上传到servlet。我认为使用异步servlet,我会立即在浏览器中获得响应,然后长任务将在另一个线程中完成。

我在这里发布我的初始代码,在为文件进程编写任何代码之前,只是一个测试异步的初始servlet。

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

@MultipartConfig
public class VideoUploader extends HttpServlet {

@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {
    doPost(request, response);
}

@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException, IOException {

    final PrintWriter pw = response.getWriter();

    final AsyncContext ac = request.startAsync();
    ac.setTimeout(80000);

    ac.addListener(new AsyncListener() {
        @Override
        public void onComplete(AsyncEvent event) throws IOException {
            System.out.println("On complete");
        }

        @Override
        public void onTimeout(AsyncEvent event) throws IOException {
            System.out.println("On timeout");
        }

        @Override
        public void onError(AsyncEvent event) throws IOException {
            System.out.println("On error");
        }

        @Override
        public void onStartAsync(AsyncEvent event) throws IOException {
            System.out.println("On start async");
        }
    });

    ac.start(new Runnable() {
        @Override
        public void run() {
            for (int i = 0; i <= 10; i++) {
                System.out.println("Async task: "
                        + Thread.currentThread().getName());
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                }
            }
            ac.complete();
        }
    });

    pw.write("end");
    pw.close();
}
}

然后,客户端部分是:

<form id="formVideo">
        <label for="videoFile">Vídeo:</label>
        <input id="videoFile" name="videoFile" type="file" /> <br/>
        <input id="uploadVideoBtn" type="button" value="Subir" onClick="uploadVideo();"/>
    </form>

    <div id="notificaciones"/>            

    <script type="text/javascript">
        function uploadVideo() {
            var file = document.getElementById("videoFile").files[0];

            var formdata = new FormData();
            formdata.append("file", file);                  

            var xhr = new XMLHttpRequest();

            xhr.open("POST","/webapp/VideoUploader", true);
            xhr.send(formdata);

            xhr.onload = function(e) {
                if (this.status == 200) {
                   alert(this.responseText);
                }
            };                  
        }               
    </script>        

当我没有将视频附加到文件输入时,过程按照我的预期完成,响应立即在浏览器中收到。但是当我附加任何大小的文件时,我的浏览器在另一个线程结束之前不会收到响应。

我正在研究非阻塞IO,但我不确定它是否与此行为有关。

我仍然不确定我是如何实现这一点的,虽然我会听取任何建议,但我想要了解这个异步servlet的行为。

2 个答案:

答案 0 :(得分:2)

很明显,您的浏览器将等到另一个线程完成。

涉及以下步骤

  1. 客户端向服务器发送请求
  2. 服务器从ThreadPool中分配线程(Servlet容器)
  3. Servlet容器创建Servlet实例/重用existsisng Servlet实例并在(Servlet线程)中调用Servcie方法
  4. 在Service方法中通过调用startAsync()将启动新线程并将请求,响应实例传递给新线程来处理请求。注意**新线程没有阻塞http连接,它只是jvm中的一个线程,此时没有任何IO闪烁
  5. Servlet线程存在服务方法并返回到线程池
    注意**此处响应尚未发送到客户端/浏览器
  6. 一旦Process在步骤4中启动完成,该线程将请求Servlet Container分配给新的Servlet线程,以将响应发送回Client。
  7. 只有在步骤6,响应才会返回给客户。因此,从客户的角度来看,普通请求和“asyncSupported = true”之间没有区别。

    Servlet 3.0通过使用“asyncSupported = true”代替每个连接的线程来支持每个请求的线程。
    每个连接的线程将导致线程饥饿。

    @WebServlet(name = "VideoUploader", urlPatterns = { "/VideoUploader" }, asyncSupported = true)
    @MultipartConfig
    public class VideoUploader extends HttpServlet {
    
    @Override
    protected void doGet(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
        doPost(request, response);
    }
    
    @Override
    protected void doPost(HttpServletRequest request,
            HttpServletResponse response) throws ServletException, IOException {
    
        final AsyncContext ac = request.startAsync();
        ac.setTimeout(80000);
    
        ac.addListener(new AsyncListener() {
    
            public void onComplete(AsyncEvent event) throws IOException {
                System.out.println("On complete");
            }
    
            public void onTimeout(AsyncEvent event) throws IOException {
                System.out.println("On timeout");
            }
    
            public void onError(AsyncEvent event) throws IOException {
                System.out.println("On error");
    
            }
    
            public void onStartAsync(AsyncEvent event) throws IOException {
                System.out.println("On start async");
    
            }
    
        });
    
        ac.start(new Runnable() {
    
            public void run() {
                System.out.println("Async task: "
                        + Thread.currentThread().getName());
                try {
                    for (Part part : ((HttpServletRequest) ac.getRequest())
                            .getParts()) {
                        System.out.println("File received"); // You Should write
                                                                // file here
                                                                // like
                                                                // part.write("fileName");
                    }
                } catch (IOException e1) {
                    e1.printStackTrace();
                } catch (ServletException e1) {
                    e1.printStackTrace();
                }
                ac.complete();
                PrintWriter pw = null;
                try {
                    pw = ac.getResponse().getWriter();
                } catch (IOException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
                }
                pw.write("end");
                pw.close();
            }
    
        });
    
    }
    }
    

答案 1 :(得分:1)

异步servlet将长时间运行的服务器端作业移交给其他服务器线程。非阻塞IO是servlet 3.1中的一项新功能,它处理传入数据阻塞或流速低于服务器可读取的情况。两者都是避免servlet线程饥饿的解决方案。他们不是要立即回复客户。

由于您使用的是Ajax,而不是常规的浏览器文件上载,如果您不关心servlet线程饥饿,它应该可以在Ajax端轻松实现甚至是同步servlet。 Ajax本质上是异步的。这是一个示例教程

http://www.javabeat.net/asynchronous-file-upload-using-ajax-jquery-progress-bar-and-java/