我们有一个简单的servlet,用于在tomcat8上实现 Apache CometEvent 以进行长轮询连接。我们使用org.apache.coyote.http11.Http11NioProtocol
时效果很好,但是,我们现在更改为使用org.apache.coyote.http11.Http11Nio2Protocol
,它将无法正常工作。
在NIO上,客户端可以通过POST与Connect servlet建立连接,而另一个客户端可以通过POST向Trigger servlet发送消息。每隔300秒,我们就会使彗星超时,客户端应用程序将再次连接彗星。
Connect servlet如下
public class Connect extends HttpServlet implements CometProcessor {
...
public void event(CometEvent event) throws IOException, ServletException {
HttpServletRequest request = event.getHttpServletRequest();
HttpServletResponse response = event.getHttpServletResponse();
if (event.getEventType() == CometEvent.EventType.BEGIN) {
String deviceid = request.getParameter("id");
MessageSender.getInstance().addConnection(deviceid, event);
request.setAttribute("org.apache.tomcat.comet.timeout", 300 * 1000);
event.setTimeout(300 * 1000);
} else if (event.getEventType() == CometEvent.EventType.ERROR) {
MessageSender.getInstance().removeConnection(event);
event.close();
} else if (event.getEventType() == CometEvent.EventType.END) {
MessageSender.getInstance().removeConnection(event);
event.close();
} else if (event.getEventType() == CometEvent.EventType.READ) {
throw new UnsupportedOperationException("This servlet does not accept data");
}
}
}
我们有另一个Trigger servlet用于向客户端发送消息:
public class Trigger extends HttpServlet {
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
byte[] receieveByteArray = ByteUtil.getHttpServletRequestBody(req);
sendTrigger(req, resp, receieveByteArray);
}
private void sendTrigger(HttpServletRequest req, HttpServletResponse resp, byte[] trigger) throws IOException, ServletException
{
try
{
MessageSender.getInstance().sendTrigger(deviceId, trigger);
} catch (Exception e)
{
logger.error("Send trigger has thrown exception: ", e);
}
}
}
MessageSender类如下
public class MessageSender
{
private static final Map<String, CometEvent> connections = new ConcurrentHashMap<String, CometEvent>();
public void addConnection(String deviceId, CometEvent event) {
connections.put(deviceId, event);
}
public void removeConnection(CometEvent event) {
while (connections.values().remove(event)) {
}
public static MessageSender getInstance() {
return instance;
}
public void sendTrigger(String deviceId, byte[] triggerMessage) throws IOException, ConnectionNotFoundException {
CometEvent comet = connections.get(deviceId);
HttpServletResponse response = comet.getHttpServletResponse();
response.addHeader("Content-Length", Integer.toString(triggerMessage.length));
response.addHeader("Content-Language", "en-US");
ServletOutputStream servletOutputStream = response.getOutputStream();
servletOutputStream.write(triggerMessage);
servletOutputStream.flush();
servletOutputStream.close();
comet.close(); // add for NIO2
connections.remove(deviceId);
}
}
我们将tomcat http协议的连接器设置更改为NIO2后,如下所示
<Connector port="8443" protocol="org.apache.coyote.http11.Http11Nio2Protocol"
maxThreads="150" SSLEnabled="true" scheme="https" secure="true"
clientAuth="false" sslProtocol="TLS" connectionTimeout="60000"
keystoreFile="D:\localhost.jks" keystorePass="******" />
事件超时不起作用,因为我们将其设置为300秒,彗星连接将在60秒后断开,我认为是连接器连接超时。并且会抛出一个例外,如下所示
28-Oct-2016 15:04:33.748 SEVERE [http-nio2-8443-exec-5] org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process Error reading request, ignored
java.lang.IllegalStateException: Reading not allowed due to timeout or cancellation
at sun.nio.ch.AsynchronousSocketChannelImpl.read(AsynchronousSocketChannelImpl.java:249)
at sun.nio.ch.AsynchronousSocketChannelImpl.read(AsynchronousSocketChannelImpl.java:297)
at org.apache.tomcat.util.net.SecureNio2Channel.read(SecureNio2Channel.java:792)
at org.apache.tomcat.util.net.Nio2Endpoint.awaitBytes(Nio2Endpoint.java:871)
at org.apache.coyote.http11.Http11Nio2Protocol$Http11ConnectionHandler.release(Http11Nio2Protocol.java:180)
at org.apache.coyote.AbstractProtocol$AbstractConnectionHandler.process(AbstractProtocol.java:722)
at org.apache.tomcat.util.net.Nio2Endpoint$SocketProcessor.doRun(Nio2Endpoint.java:1073)
at org.apache.tomcat.util.net.Nio2Endpoint$SocketProcessor.run(Nio2Endpoint.java:1032)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:745)
如果客户端在此之后再次进行彗星连接,则另一个客户端尝试向Trigger servlet发送消息。彗星将立即结束并断开连接。
感谢任何帮助