我正在尝试为服务器上长时间运行的进程实现一个解决方案,在服务器上处理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中的异步支持。
你也可以使用氛围库,它会为你做这件事,效果很好。