当页面加载时,我已经注册了一个典型的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说,“事件源已经打开了!”成功了
我的服务器代码是一个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() – 方法,并检索新的数据.
问题:
不过,我想知道如何通过使用新的Servlet 3.0 Futures(如Async-Support或asyncContext.addListener(asyncListener))或者其他不知道的内容来更好地做出这个代码.当我从来没有关闭流,我不知道我的服务器将如何扩展?
理论上,最好的方法是当新数据存在时,通过服务器端代码显式触发doGet() – 方法,所以客户端不需要触发客户端“onmessage()” – 因此,服务器端“doGet()” – 每3秒钟为新数据的方法.
解决方法
这是一个很好的问题,这里是一个完整的工作示例(Servlet 3.0 / Java EE 6)
一些注释:
>它通过调用flush()的out.checkError()处理“浏览器选项卡/窗口关闭”
我写的很快,所以我确信它可以改进,只是一个POC,在测试前不要在生产中使用
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,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>