在服务器端,我有一个简单的servlet接受请求并创建一个虚拟事件创建者任务:
private Executor executor = Executors.newSingleThreadExecutor(); public void doGet(final HttpServletRequest request,final HttpServletResponse response) { final AsyncContext asynCtx = request.startAsync(request,response); response.setHeader("Cache-Control","no-cache"); response.setContentType("text/event-stream"); response.setCharacterEncoding("utf-8"); executor.execute(() -> { boolean run = true; try { while (run) { final ServletResponse resp = asynCtx.getResponse(); run = resp != null; if (resp != null) { System.out.println("pushing a server event."); final PrintWriter writer = asynCtx.getResponse().getWriter(); writer.println("data: {time: " + System.currentTimeMillis() + "}\n"); writer.flush(); } else { System.out.println("stopping beeper,no response object available anymore."); break; // do not run anymore,we got no response } Thread.sleep(2000); } } catch (final Exception e) { e.printStackTrace(); } }); }
在客户端我简单地说:
$(document).ready(function () { var source = new EventSource("/events"); source.onmessage = function (event) { console.log("received event: " + JSON.stringify(event)); document.getElementById("eventContainer").innerHTML += event.data + "<br/>"; }; console.log("start to receive events...") });
当我加载HTML文件时,它工作正常,事件被接收并写入控制台.但是30秒后我收到一条错误消息:
GET [HttpOfLocalhost] / events net :: ERR_INCOMPLETE_CHUNKED_ENCODING
为什么?
请求被杀死并立即启动一个新的,所以它不会杀死应用程序,但控制台上的错误消息并不好.
我的开发者控制台截图:
请求/ resposne详细信息:
时间,这表明它总是在30秒后发生:
谢谢!
解决方法
AsyncContext对象具有setTimeout(…)方法.默认情况下,我的tomcat版本(Tomcat嵌入式8)的值设置为30,000毫秒(30秒).这正是我在chrome控制台中遇到net :: ERR_INCOMPLETE_CHUNKED_ENCODING错误后的持续时间.
我检查使用:
System.out.println("Current Timeout is: " + asynCtx.getTimeout() + " ms");
显示:
Current Timeout is: 30000 ms
所以为了避免net:ERR消息,有人可以将超时设置为0.但是比事件线程永远运行(不幸的是).
我使用的另一种解决方案是将AsyncListener添加到AsyncContext并在onTimeout()方法中调用complete()方法.
来自complete()方法的API文档:
Completes the asynchronous operation that was started on the request
that was used to initialze this AsyncContext,closing the response
that was used to initialize this AsyncContext. Any listeners of type
AsyncListener that were registered with the ServletRequest for which
this AsyncContext was created will be invoked at their onComplete
method.
我的听众的源代码:
asynCtx.addListener(new AsyncListener() { @Override public void onComplete(AsyncEvent asyncEvent) throws IOException { System.out.println("onComplete(...)"); } @Override public void onTimeout(AsyncEvent asyncEvent) throws IOException { // this will close the request and the context gracefully // and the net:ERR is gone. asyncEvent.getAsyncContext().complete(); System.out.println("onTimeout(...)"); } @Override public void onError(AsyncEvent asyncEvent) throws IOException { System.out.println("onError(...)"); } @Override public void onStartAsync(AsyncEvent asyncEvent) throws IOException { System.out.println("onStart(...)"); } });
是的,这是由于缺乏知识.我希望这对某人有帮助.