deferredresult 是 spring mvc 实现长轮询的核心机制,基于 servlet 3.0+ 异步规范,请求进入 controller 后立即释放 tomcat 线程,由业务线程显式调用 setresult() 触发响应,支持超时控制、事件驱动唤醒与定向推送。

Spring MVC 支持长轮询(Long Polling),核心是利用 Servlet 3.0+ 的异步能力,配合 DeferredResult 或 Callable 实现服务端挂起响应、延迟返回。关键不是“立刻给结果”,而是“先承诺会回,等有数据再写”。
用 DeferredResult 实现可控的长轮询
DeferredResult 是最推荐的方式,它把响应生命周期交由业务代码显式控制,适合需要主动触发返回(如监听消息队列、事件总线、定时检查等)的场景。
- 控制器方法直接返回
DeferredResult<string></string>(泛型可为任意序列化类型,如ResponseEntity>) - 创建时可设超时时间(单位毫秒),超时后自动触发默认响应(避免客户端无限等待)
- 在后台线程中调用
setResult()写入响应体,或setErrorResult()报错,两者都会立即结束请求 - 注意:
DeferredResult实例不能复用,每次请求都需新建
配合 Spring 事件或外部通知触发响应
长轮询的价值在于“有变化才推”,所以通常要结合某种状态变更信号。常见做法:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 用
ApplicationEventPublisher发布自定义事件,监听器收到后遍历待唤醒的DeferredResult并调用setResult() - 使用
ConcurrentHashMap<string deferredresult></string>缓存未完成的请求(例如按用户 ID 或会话 ID 索引),便于定向推送 - 搭配 Redis Pub/Sub 或 Kafka 消费逻辑,在消息到达时批量唤醒相关请求
前端发起长轮询请求的注意事项
客户端行为直接影响服务端资源占用和用户体验:
- 每次请求完成后,应立即发起下一次请求(避免空窗期),但建议加小随机抖动(如 ±500ms),防止请求洪峰
- 设置合理的请求超时(略大于服务端
DeferredResult超时),并处理 503/timeout 响应后重试 - 若用 fetch,需禁用缓存:
cache: 'no-store';若用 XMLHttpRequest,设置xhr.setRequestHeader('Cache-Control', 'no-cache')
配置与边界处理不可少
异步请求不等于无约束,必须做好基础防护:
- 在
WebMvcConfigurer中通过configureAsyncSupport设置全局异步超时(默认 10 秒),并注册CallableProcessingInterceptor或DeferredResultProcessingInterceptor处理超时/异常 - 限制单个 IP 或用户并发长轮询请求数(如用 Guava RateLimiter 或 Spring Cache + 计数器),防资源耗尽
- 确保 Tomcat/Jetty 的连接数、线程池(尤其是 async-supported 的容器线程)配置足够,避免因容器线程阻塞导致异步失效
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










