使用Ajax和线程在服务器上处理长时间运行的jsp请求



我正在尝试为服务器上长时间运行的进程实现一个解决方案,在服务器上处理pdf生成请求大约需要10分钟。浏览器在5分钟时无聊/超时。我想使用Ajax和线程来处理这个问题。我使用的是ajax的常规javascript。但我被它卡住了。

我已经到达了它将请求发送到servlet并且servlet启动线程的地步。请参阅以下代码

public class HelloServlet extends javax.servlet.http.HttpServlet 
     implements javax.servlet.Servlet {
    protected void doGet(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {      
    }   
    protected void doPost(HttpServletRequest request, HttpServletResponse response) 
        throws ServletException, IOException {
        System.out.println("POST request!!");
        LongProcess longProcess = new LongProcess();
        longProcess.setDaemon(true);
        longProcess.start();
        request.getSession().setAttribute("longProcess", longProcess);
        request.getRequestDispatcher("index.jsp").forward(request, response);
    }   
 }
    class LongProcess extends Thread {
        public void run() {
            System.out.println("Thread Started!!");
            while (progress < 10) {             
                try { sleep(2000); } catch (InterruptedException ignore) {}
                progress++;
            }
        }           
    }

这是我的AJax呼叫

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>My Title</title>
<script language="JavaScript" >
function getXMLObject()  //XML OBJECT
    {
       var xmlHttp = false;
         xmlHttp = new XMLHttpRequest();        //For Mozilla, Opera Browsers
       return xmlHttp;  // Mandatory Statement returning the ajax object created
    }
    var xmlhttp = new getXMLObject();   //xmlhttp holds the ajax object
    function ajaxFunction() {
        xmlhttp.open("GET","HelloServlet" ,true);
        xmlhttp.onreadystatechange  = handleServerResponse;
        xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
        xmlhttp.send(null);
      }
    function handleServerResponse() {
       if (xmlhttp.readyState == 4) {
         if(xmlhttp.status == 200) {           
           document.forms[0].myDiv.value = xmlhttp.responseText;
           setTimeout(ajaxFunction(), 2000);
         }
         else {
            alert("Error during AJAX call. Please try again");
         }
       }
    }
    function openPDF() {    
           document.forms[0].method = "POST";
        document.forms[0].action = "HelloServlet";
        document.forms[0].submit();     
    }
    function stopAjax(){
         clearInterval(intervalID);
     }
</script>
</head> 
<body><form name="myForm">
<table><tr><td>
<INPUT TYPE="BUTTON" NAME="Download" VALUE="Download Queue ( PDF )" onclick="openPDF();">
</td></tr>
<tr><td>
Current status: <div id="myDiv"></div>%
</td></tr></table>
</form></body></html>

但我不知道如何进一步处理,比如线程将如何与浏览器沟通进程已经完成,ajax应该如何调用我并检查请求的状态。

如果我遗漏了一些碎片,请告诉我。任何有帮助的建议。

Jetty 9.0 Distribution包含一个长轮询聊天示例,其中包括一个协同工作的异步servlet和javascript客户端。轮询请求由客户端发起,这会导致servlet启动一个异步循环,该循环故意在选定的时间间隔内超时。有些浏览器会持续很长时间,但有些浏览器只持续30秒。因此,建议将超时时间设置为小于30秒。当servlet超时时,它会向客户端发送一个信号,导致客户端初始化另一个轮询。数据可以在任何时候通过响应发送,如果需要,客户端可以在之后再次连接。这具有建立从服务器到客户端的开放通道的效果。

// This starts an async request
AsyncContext asyncCtx = request.startAsync();
asyncCtx.setTimeout(10000);   // 10 seconds
asyncCtx.addListener(member);
// This is the timeout handler which tells the client to continue to poll
@Override
public void onTimeout(AsyncEvent event) throws IOException {
    System.out.println("Client onTimeoutrn");
    AsyncContext asyncCtx = asyncCtxAtomRef.get();
    if ((asyncCtx != null) && asyncCtxAtomRef.compareAndSet(asyncCtx, null))
    {
        HttpServletResponse response = (HttpServletResponse)asyncCtx.getResponse();
        response.setContentType("text/json;charset=utf-8");
        PrintWriter out=response.getWriter();
        out.print("{action:"poll"}");
        asyncCtx.complete();
    }
}

从本质上讲,任何通过"轮询"操作发送给客户端的响应都会导致客户端自动重新连接。它似乎运行得很好,所以你可能想看看。

您可以使用Servlet 3.0异步处理,也可以使用现有的库,如氛围(在下面使用Servlet 3.0)。

这个想法是调用servlet并启动AsyncContext。然后将该上下文传递给线程,并使用它定期发送一些进度。在客户端读取这个流有点棘手,请参阅:jquery ajax,增量读取流?如果访问线程内的原始HttpServletResponse,它将不起作用。

这是一个示例代码:

@WebServlet("/hello" asyncSupported=true)
public class HelloServlet extends HttpServlet {
    public void doGet(HttpServletRequest req, HttpServletResponse res) {
        AsyncContext async = request.startAsync(req, res);
        LongProcess longProcess = new LongProcess();
        longProcess.setDaemon(true);
        longProcess.start();
    }
}
class LongProcess extends Thread {
    private final AsyncContext asyncContext;
    public LongProcess(AsyncContext asyncContext) {
        this.asyncContext = asyncContext;
    }
    public void run() {
        System.out.println("Thread Started!!");
        while (progress < 10) {             
            try { sleep(2000); } catch (InterruptedException ignore) {}
            progress++;
            //use asyncContext here to send progress to the client incrementally
        }
    }           
}

另请参阅Servlet 3.0中的异步支持。

你也可以使用氛围库,它会为你做这件事,效果很好。

相关内容

最新更新