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

 更新时间:2025年07月15日 09:30:53   作者:超级小忍  
长轮询(Long Polling)是一种经典的 HTTP 轮询机制,本文将结合 Spring Boot,从异步处理、连接复用、客户端优化等角度出发,详细讲解如何优化长轮询机制,有需要的可以了解下

一、前言

长轮询(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长轮询内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • 设置Myeclipse中的代码格式化、注释模板及保存时自动格式化

    设置Myeclipse中的代码格式化、注释模板及保存时自动格式化

    这篇文章主要介绍了设置Myeclipse中的代码格式化、注释模板及保存时自动格式化方法,需要的朋友可以参考下
    2014-10-10
  • Java8 CompletableFuture 异步执行操作

    Java8 CompletableFuture 异步执行操作

    CompletableFuture是java8提供的基于异步操作的封装,日常开发中经常会用到,接下来通过本文给大家介绍Java8 CompletableFuture 异步执行操作,感兴趣的朋友一起看看吧
    2021-06-06
  • SpringBoot日志的使用解读

    SpringBoot日志的使用解读

    本文主要介绍了SpringBoot中日志的使用方法,包括默认输出格式、使用Lombok简化日志代码、通过yml和xml配置日志等
    2025-02-02
  • 详解spring boot集成ehcache 2.x 用于hibernate二级缓存

    详解spring boot集成ehcache 2.x 用于hibernate二级缓存

    本篇文章主要介绍了详解spring boot集成ehcache 2.x 用于hibernate二级缓存,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-05-05
  • Java中默认的访问权限作用域解析

    Java中默认的访问权限作用域解析

    这篇文章主要介绍了Java中默认的访问权限作用域,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • springboot多项目结构实现

    springboot多项目结构实现

    本文主要介绍了springboot多项目结构实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • Java中字符串常见题之String相关讲解

    Java中字符串常见题之String相关讲解

    今天小编就为大家分享一篇关于Java中字符串常见题之String相关讲解,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧
    2019-01-01
  • JAVA记住密码功能的实现代码

    JAVA记住密码功能的实现代码

    这篇文章主要介绍了JAVA记住密码功能的实现代码,代码简单易懂,非常不错,具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-01-01
  • Java实现XML格式与JSON格式互相转换的方法

    Java实现XML格式与JSON格式互相转换的方法

    这篇文章主要介绍了Java实现XML格式与JSON格式互相转换的方法,方法通过实例代码给大家介绍的非常详细,选择使用哪种格式通常取决于项目的需求和上下文,所以格式转换就成了我们必备的技能,具体实现代码跟随小编一起看看吧
    2023-10-10
  • Java结合uniapp实现验证码功能的示例详解

    Java结合uniapp实现验证码功能的示例详解

    UniApp 是一个基于 Vue.js 的跨平台应用开发框架,允许开发者使用统一的代码库来构建多平台应用,这篇文章将给大家介绍Java结合uniapp实现验证码功能,文中通过详细的代码示例讲解的非常详细,需要的朋友可以参考下
    2024-07-07

最新评论