Вопрос по html5 – SSE и Servlet 3.0
Я зарегистрировал типичный SSE при загрузке страницы:
Client:
sseTest: function(){
var source = new EventSource('mySSE');
source.onopen = function(event){
console.log("eventsource opened!");
};
source.onmessage = function(event){
var data = event.data;
console.log(data);
document.getElementById('sse').innerHTML+=event.data + "<br />";
};
}
Мой Javascript-отладчик говорит, что "источник событий открыт!" был успешно.
My Server Code is a Servlet 3.0:
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Random;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet(urlPatterns={"/mySSE"}, name = "hello-sse", asyncSupported=true)
public class MyServletSSE extends HttpServlet {
@Override
public void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/event-stream");
resp.setCharacterEncoding("UTF-8");
Random random = new Random();
PrintWriter out = resp.getWriter();
//AsyncContext aCtx = req.startAsync(req, resp);
//ServletRequest sReq = aCtx.getRequest();
String next = "data: " + String.valueOf(random.nextInt(100) + 1) + "\n\n";
//out.print("retry: 600000\n"); //set the timeout to 10 mins in milliseconds
out.write(next);
out.flush();
// do not close the stream as EventSource is listening
//out.close();
//super.doGet(req, resp);
}
}
Код работает! Клиентский код запускает метод doGet () - каждые 3 секунды и получает новые данные.
Questions: Однако мне интересноhow I can make this code better с помощью нового Servlet 3.0 Futures, такого как Async-Support или asyncContext.addListener (asyncListener) или чего-то еще, чего я не знаю. Интересно, я никогда не закрываю потокhow my server will scale?
Теоретически, лучшим подходом было бы явным образом запускать метод doGet () через серверный код при наличии новых данных, поэтому клиенту не нужно запускать на стороне клиента «onmessage ()» -Method и поэтому на стороне сервера «doGet ()» - метод каждые 3 секунды для новых данных.
вот полный рабочий пример (Servlet 3.0 / Java EE 6)
Некоторые заметки:
it handles "browser tab / window closed" viaout.checkError()
that also calls flush()
I wrote it quickly, so I'm sure it can be improved, just a POC, don't use in production before testing
Servlet: (для краткости опущен импорт, скоро будет обновлена полная версия)
@WebServlet(urlPatterns = {"/mySSE"}, asyncSupported = true)
public class MyServletSSE extends HttpServlet {
private final Queue<AsyncContext> ongoingRequests = new ConcurrentLinkedQueue<>();
private ScheduledExecutorService service;
@Override
public void init(ServletConfig config) throws ServletException {
final Runnable notifier = new Runnable() {
@Override
public void run() {
final Iterator<AsyncContext> iterator = ongoingRequests.iterator();
//not using for : in to allow removing items while iterating
while (iterator.hasNext()) {
AsyncContext ac = iterator.next();
Random random = new Random();
final ServletResponse res = ac.getResponse();
PrintWriter out;
try {
out = res.getWriter();
String next = "data: " + String.valueOf(random.nextInt(100) + 1) + "num of clients = " + ongoingRequests.size() + "\n\n";
out.write(next);
if (out.checkError()) { //checkError calls flush, and flush() does not throw IOException
iterator.remove();
}
} catch (IOException ignored) {
iterator.remove();
}
}
}
};
service = Executors.newScheduledThreadPool(10);
service.scheduleAtFixedRate(notifier, 1, 1, TimeUnit.SECONDS);
}
@Override
public void doGet(HttpServletRequest req, HttpServletResponse res) {
res.setContentType("text/event-stream");
res.setCharacterEncoding("UTF-8");
final AsyncContext ac = req.startAsync();
ac.setTimeout(60 * 1000);
ac.addListener(new AsyncListener() {
@Override public void onComplete(AsyncEvent event) throws IOException {ongoingRequests.remove(ac);}
@Override public void onTimeout(AsyncEvent event) throws IOException {ongoingRequests.remove(ac);}
@Override public void onError(AsyncEvent event) throws IOException {ongoingRequests.remove(ac);}
@Override public void onStartAsync(AsyncEvent event) throws IOException {}
});
ongoingRequests.add(ac);
}
}
JSP:
<%@page contentType="text/html" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>JSP Page</title>
<script>
function test() {
var source = new EventSource('mySSE');
source.onopen = function(event) {
console.log("eventsource opened!");
};
source.onmessage = function(event) {
var data = event.data;
console.log(data);
document.getElementById('sse').innerHTML += event.data + "<br />";
};
}
window.addEventListener("load", test);
</script>
</head>
<body>
<h1>Hello SSE!</h1>
<div id="sse"></div>
</body>
</html>
Люди могут получить «IllegalStateException: не поддерживается» для startAsync (), в этом случае либо не забудьте:
@WebServlet(urlPatterns = "/Sse", asyncSupported=true)
или использовать
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
отэтот сообщение