Tomcat8 NIO2协议是否支持Apache CometEvent?

时间:2016-10-28 07:24:22

标签: sockets tomcat nio comet nio2

  • 如何在NIO2协议上设置彗星事件超时?
  • 如何很好地处理NIO2协议上的套接字连接?(例如,关闭连接)

我们有一个简单的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发送消息。彗星将立即结束并断开连接。

感谢任何帮助

0 个答案:

没有答案