我在页面加载时注册了典型的SSE:
客户端:
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-Debugger说,“eventsource已经打开了!”成功了。
我的服务器代码是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);
}
}
代码有效!客户端代码每3秒触发一次doGet() - Method并检索新数据。
问题: 但是,我想通过使用新的Servlet 3.0 Futures(例如Async-Support或asyncContext.addListener(asyncListener)或其他我不知道的东西)如何使代码更好。由于我从不关闭流,我想知道我的服务器将如何扩展?
理论上,最好的方法是在新数据存在时通过服务器端代码显式触发doGet() - 方法,因此客户端不需要触发客户端“onmessage()” - 方法因此服务器端“doGet()” - 每3秒对新数据进行一次方法。
答案 0 :(得分:12)
这是一个很好的问题,这是一个完整的工作示例(Servlet 3.0 / Java EE 6)
一些注意事项:
out.checkError()
处理“浏览器标签/窗口关闭”,同时调用flush()
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>
答案 1 :(得分:1)
有用的例子。
对于startAsync(),人们可能会得到“IllegalStateException:不支持”,在这种情况下要么不要忘记:
@WebServlet(urlPatterns = "/Sse", asyncSupported=true)
或使用
request.setAttribute("org.apache.catalina.ASYNC_SUPPORTED", true);
来自this帖子