You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Tomcat 9中基于异步事件驱动Servlet实现长轮询的最佳实践

Tomcat 9 实现HTTPS长轮询的最佳实践

核心方案:异步事件驱动Servlet(Async Servlet)

Tomcat 9完全支持异步Servlet,这正是解决同步I/O线程占用问题的最优方案——它能通过固定线程池处理大量长连接,无需为每个连接分配独立线程,完美适配你的需求。

实现步骤

  1. 编写支持异步的Servlet
    通过注解开启异步支持,同时维护异步上下文队列用于消息推送,搭配固定线程池处理后台逻辑:

    @WebServlet(urlPatterns = "/long-poll", asyncSupported = true)
    public class LongPollServlet extends HttpServlet {
        // 线程安全队列,保存待推送的异步连接上下文
        private final Queue<AsyncContext> asyncContexts = new ConcurrentLinkedQueue<>();
        // 固定大小线程池,处理消息推送任务
        private final ExecutorService pushExecutor = Executors.newFixedThreadPool(8);
    
        @Override
        protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
            // 配置HTTPS长连接响应头
            resp.setContentType("text/plain;charset=UTF-8");
            resp.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
            resp.setHeader("Pragma", "no-cache");
            resp.setHeader("Connection", "keep-alive");
    
            // 启动异步上下文
            AsyncContext asyncContext = req.startAsync();
            // 设置超时时间,避免连接长期挂起
            asyncContext.setTimeout(30000);
    
            // 添加异步监听器,处理超时、完成等事件
            asyncContext.addListener(new AsyncListener() {
                @Override
                public void onTimeout(AsyncEvent event) throws IOException {
                    asyncContexts.remove(asyncContext);
                    AsyncContext ac = event.getAsyncContext();
                    ac.getResponse().getWriter().write("timeout");
                    ac.complete();
                }
    
                @Override
                public void onComplete(AsyncEvent event) {
                    asyncContexts.remove(asyncContext);
                }
    
                @Override public void onStartAsync(AsyncEvent event) {}
                @Override public void onError(AsyncEvent event) {}
            });
    
            // 将上下文加入队列,等待消息触发
            asyncContexts.add(asyncContext);
        }
    
        // 消息推送方法,实际场景可从MQ、业务逻辑中触发
        public void broadcastMessage(String message) {
            pushExecutor.submit(() -> {
                Iterator<AsyncContext> iterator = asyncContexts.iterator();
                while (iterator.hasNext()) {
                    AsyncContext ac = iterator.next();
                    try {
                        ac.getResponse().getWriter().write(message);
                        ac.complete(); // 完成响应,释放资源
                        iterator.remove();
                    } catch (IOException e) {
                        iterator.remove();
                        e.printStackTrace();
                    }
                }
            });
        }
    }
    
  2. Tomcat NIO连接器优化
    在server.xml中配置HTTPS的NIO连接器,适配异步场景:

    <Connector port="443" protocol="org.apache.coyote.http11.Http11NioProtocol"
               maxThreads="200" minSpareThreads="30"
               asyncTimeout="30000"
               SSLEnabled="true" scheme="https" secure="true"
               keystoreFile="/your/keystore/path" keystorePass="your-password"
               sslProtocol="TLSv1.2"/>
    
    • maxThreads:设置核心工作线程数,异步模式下无需过大,因为连接等待不占用线程
    • asyncTimeout:全局异步超时时间,与Servlet内的配置配合生效

核心优势

  • 低资源占用:长连接等待状态不消耗Tomcat工作线程,仅在推送消息时使用固定线程池
  • 完全兼容HTTPS:基于标准HTTP/HTTPS协议,使用443端口,满足端口限制要求
  • 替代废弃的Comet API:Async Servlet是Servlet 3.0+标准特性,Tomcat 9原生支持,无兼容性风险

额外优化建议

  • 连接复用:确保客户端和服务器开启HTTP Keep-Alive,减少TCP握手开销
  • 批量推送:针对大量客户端,可批量处理异步上下文,避免线程池过载
  • 客户端重连:客户端收到超时或响应后,自动重新发起长轮询,保持连接活性
  • 内存清理:通过异步监听器及时移除失效上下文,防止内存泄漏

内容的提问来源于stack exchange,提问作者vmayorow

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 05:00:05