欢迎来到徐庆高(Tea)的个人博客网站
磨难很爱我,一度将我连根拔起。从惊慌失措到心力交瘁,我孤身一人,但并不孤独无依。依赖那些依赖我的人,信任那些信任我的人,帮助那些给予我帮助的人。如果我愿意,可以分裂成无数面镜子,让他们看见我,就像看见自己。察言观色和模仿学习是我的领域。像每个深受创伤的人那样,最终,我学会了随遇而安。
当前位置: 日志文章 > 详细内容

SpringBoot如何化长轮询连接频繁建立销毁问题

2025年07月15日 Java
一、前言长轮询(long polling)是一种经典的 http 轮询机制,它在不支持 websocket 或 server-sent events(sse)的环境中,仍然是一种实现“伪实

一、前言

长轮询(long polling)是一种经典的 http 轮询机制,它在不支持 websocket 或 server-sent events(sse)的环境中,仍然是一种实现“伪实时”通信的有效方式。然而,长轮询的一个显著缺点是:每次请求都需要建立和销毁连接,频繁的 http 请求会造成服务器资源的浪费

本文将结合 spring boot,从异步处理、连接复用、客户端优化等角度出发,详细讲解如何优化长轮询机制,降低服务器负载,同时保持一定的实时性。

二、长轮询的基本实现(spring boot 示例)

1. controller 示例代码

@restcontroller
public class pollingcontroller {

    private string latestdata = "no new data";
    private final list<deferredresult<string>> results = new copyonwritearraylist<>();

    @getmapping("/poll")
    public deferredresult<string> longpolling() {
        deferredresult<string> result = new deferredresult<>(5000l, "timeout");
        results.add(result);

        result.oncompletion(() -> results.remove(result));
        result.ontimeout(() -> result.setresult("timeout"));

        return result;
    }

    @postmapping("/update")
    public void updatedata(@requestbody map<string, string> payload) {
        this.latestdata = payload.get("data");
        results.foreach(result -> result.setresult(latestdata));
        results.clear();
    }
}

2. 客户端 javascript 示例

function startpolling(lastversion = "") {
  fetch(`/poll?lastversion=${lastversion}`)
    .then((res) => res.text())
    .then((data) => {
      console.log("received:", data)
      startpolling(data) // 下一轮轮询
    })
    .catch((err) => {
      console.error("polling failed:", err)
      settimeout(startpolling, 5000) // 失败后重试
    })
}

startpolling()

三、优化策略详解(spring boot 实践)

1. 使用deferredresult实现异步非阻塞处理

原理:

spring boot 支持通过 deferredresult 将请求从主线程中释放,避免阻塞线程池资源。

优势:

  • 避免线程阻塞,提高并发处理能力;
  • 更好地管理长轮询请求;
  • 可设置超时、异常处理等回调。

示例代码(已在上面展示):

使用 deferredresult 替代传统的 wait/notify 同步方式。

2. 合理设置超时时间与客户端轮询间隔

服务端配置(application.yml):

spring:
  mvc:
    async:
      request-timeout: 0 # 不超时,由 deferredresult 控制

客户端优化建议:

  • 高实时性场景:超时时间设为 3~5 秒,客户端 2~3 秒发起一次请求;
  • 低实时性场景:超时时间设为 10~30 秒,客户端 10 秒发起一次请求;

3. 使用 http/2 提升连接复用效率

配置 spring boot 支持 http/2:

生成自签名证书(开发环境):

keytool -genkeypair -alias http2 -keyalg rsa -keysize 2048 -storetype pkcs12 -keystore http2.p12 -validity 3650

配置 application.yml

server:
  port: 8443
  ssl:
    key-store: classpath:http2.p12
    key-store-password: yourpassword
    key-store-type: pkcs12
    key-alias: http2
  http2:
    enabled: true

依赖中添加:

<dependency>
    <groupid>org.springframework.boot</groupid>
    <artifactid>spring-boot-starter-tomcat</artifactid>
</dependency>
<dependency>
    <groupid>org.apache.tomcat</groupid>
    <artifactid>tomcat-embed-jasper</artifactid>
</dependency>

效果:

  • 一个 tcp 连接上可处理多个请求;
  • 减少 tcp 连接建立和 tls 握手开销;
  • 显著提升长轮询性能。

4. 客户端智能重试与退避算法

javascript 示例:

let retrycount = 0

function startpolling() {
  fetch("/poll")
    .then((res) => res.text())
    .then((data) => {
      console.log("received:", data)
      retrycount = 0
      startpolling() // 成功后继续轮询
    })
    .catch((err) => {
      const delay = math.min(1000 * math.pow(2, retrycount), 30000) // 最大30秒
      console.log(`retrying in ${delay}ms`)
      settimeout(startpolling, delay)
      retrycount++
    })
}

优势:

  • 避免网络不稳定时频繁请求;
  • 减轻服务器压力;
  • 提升用户体验。

5. 使用缓存机制减少重复请求

思路:

客户端传入上次收到的数据版本号,服务端仅在有新数据时才响应。

controller 示例:

@getmapping("/poll")
public deferredresult<string> poll(@requestparam(required = false) string lastversion) {
    if (latestdata.equals(lastversion)) {
        deferredresult<string> result = new deferredresult<>(10_000l);
        results.add(result);
        result.oncompletion(() -> results.remove(result));
        return result;
    } else {
        return new deferredresult<>(latestdata);
    }
}

客户端传参:

startpolling("v1.0")

四、对比与建议

优化策略是否适合 spring boot优势推荐程度
使用 deferredresult避免线程阻塞,提升并发能力非常推荐
设置合理超时时间平衡实时性与资源消耗推荐
使用 http/2是(需配置)减少连接建立开销推荐
客户端退避算法提高容错能力,减轻服务器压力推荐
结合缓存机制避免重复请求推荐

五、结语

虽然长轮询不是最高效的实时通信方式,但在某些场景下(如兼容性要求高、环境限制)仍然具有实用价值。通过结合 spring boot 提供的异步处理机制、http/2 特性、客户端智能重试等优化手段,我们可以显著降低连接频繁建立销毁带来的资源消耗,同时提升系统的稳定性和性能。

如果你对实时性要求更高,建议优先考虑 server-sent events(sse)websocket,它们更适合现代 web 应用的实时通信需求。

到此这篇关于springboot如何化长轮询连接频繁建立销毁问题的文章就介绍到这了,更多相关springboot长轮询内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!