Обработка долгосрочного jsp-запроса на сервере с использованием Ajax и потоков
Я пытаюсь реализовать решение для длительного процесса на сервере, где обработка запроса на создание PDF занимает около 10 минут. Браузер скучно / тайм-аут на 5 минут. Я думал, чтобы справиться с этим с помощью Ajax и потоков. Я использую обычный JavaScript для AJAX. Но я застрял с этим.
Я дошел до того момента, когда он отправляет запрос сервлету, и сервлет запускает поток. Пожалуйста, смотрите код ниже
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 позвонил мне и проверил состояние запроса.
Пожалуйста, дайте мне знать, если я что-то упустил. Любое предложение, если полезно.
2 ответа
Распределение Jetty 9.0 включает пример длинного опроса чата, который включает асинхронный сервлет и клиент JavaScript, которые работают в тандеме. Запрос опроса инициируется клиентом, в результате чего сервлет запускает асинхронный цикл, который намеренно истекает через выбранный интервал. Некоторые браузеры работают долго, а некоторые - только 30 секунд. Поэтому рекомендуется установить время ожидания менее 30 секунд. Когда сервлет истекает, он отправляет клиенту сигнал, заставляющий клиента инициировать другой опрос. Данные могут быть отправлены в любое время через ответ, и клиент может просто подключиться снова после этого, если это необходимо. Это приводит к установлению открытого канала от сервера к клиенту.
// 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 onTimeout\r\n");
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, либо существующие библиотеки, например атмосферу (с использованием сервлета 3.0 внизу).
Идея состоит в том, чтобы вызвать сервлет и запустить 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.
Вы также можете использовать библиотеку атмосферы, которая сделает это за вас и работает довольно хорошо.