Spring之SseEmitter实现让你的进度条实时更新

 更新时间:2025年02月28日 11:13:12   作者:drebander  
Spring SseEmitter是一种实现服务器端推送事件(SSE)的机制,支持单向通信,适用于实时数据传输需求,通过代码示例和应用场景分析,展示了如何在服务端和客户端使用SseEmitter进行实时数据推送

在现代 Web 应用中,实时数据传输已经成为一个不可忽视的需求,尤其是在聊天系统、实时数据展示、进度推送等场景中。

Spring 提供了 SseEmitter 来支持服务器端推送事件(Server-Sent Events, SSE),它通过 HTTP 协议允许服务器主动向客户端推送数据。与 WebSocket 不同,SSE 是单向通信,但它也提供了一种轻量、高效的实时数据流解决方案。

本文将从原理、实际应用、客户端和服务端代码示例、扩展应用场景等方面详细介绍 Spring SseEmitter

1. 原理解析

1.1 Server-Sent Events (SSE)

SSE 是一种基于 HTTP 协议的单向数据流技术,允许服务器通过持久化的 HTTP 连接将事件推送到客户端。其与 WebSocket 的主要区别是,SSE 是单向通信(从服务器到客户端),而 WebSocket 是全双工通信(服务器和客户端都可以发送数据)。

在 SSE 中,客户端通过 EventSource 对象与服务器建立连接,服务器将数据以流的方式推送到客户端。SSE 使用标准的 HTTP 协议,并且是基于文本的流,数据通常以 text/event-stream 类型的格式传输。

1.2 SseEmitter

SseEmitter 是 Spring 提供的一个类,用于实现 SSE 功能。它支持向客户端推送数据,并且能够处理长连接。SseEmitter 的主要功能包括:

  • 异步发送数据:SSE 是长连接,SseEmitter 可以在服务器端异步发送数据。
  • 支持超时管理:SseEmitter 可以设置超时,若客户端在指定时间内没有响应,连接会自动关闭。
  • 异常处理:如果发生错误,可以通过 SseEmitter 完成错误通知和连接关闭。

2. 服务端代码实现

2.1 创建 SSE 连接

使用 Spring 的 SseEmitter 来向客户端推送实时事件非常简单。

我们通过 @GetMapping 注解来映射一个请求路径,返回一个 SseEmitter 实例。

@RestController
public class SseController {

    @GetMapping(value = "/sync/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter syncStream() {
        SseEmitter emitter = new SseEmitter();

        // 模拟异步任务,发送实时数据
        new Thread(() -> {
            try {
                for (int i = 0; i <= 100; i++) {
                    emitter.send("data: " + i + "% complete\n\n");
                    Thread.sleep(1000);  // 模拟任务处理
                }
                emitter.complete();  // 任务完成,结束 SSE 流
            } catch (Exception e) {
                emitter.completeWithError(e);  // 出现异常时结束流
            }
        }).start();

        return emitter;
    }
}

在上面的代码中,syncStream 方法创建了一个 SseEmitter 实例,并使用一个线程模拟了一个处理任务的过程。

每秒钟将当前进度推送给客户端,直到进度达到 100%。当任务完成时,通过 emitter.complete() 关闭连接。

  • SseEmitter.send(data): 用于向客户端推送数据。
  • SseEmitter.complete(): 用于标记推送完成,关闭连接。
  • SseEmitter.completeWithError(exception): 如果发生异常,可以通过此方法结束连接并发送错误信息。

2.2 超时设置

SSE 连接可能会因为网络问题或其他原因被中断,Spring 提供了超时设置,确保连接不被无限期阻塞。

@GetMapping(value = "/sync/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public SseEmitter syncStream() {
    SseEmitter emitter = new SseEmitter(30000L);  // 设置超时时间为30秒
    // 发送数据的代码同上...
    return emitter;
}

3. 客户端代码实现

在客户端,我们可以使用原生的 JavaScript EventSource 对象来接收 SSE 流数据。EventSource 会自动管理连接,包括重试和重新连接等操作。

let eventSource = new EventSource("/sync/stream");

eventSource.onmessage = function(event) {
    console.log("Received message: ", event.data);
};

eventSource.onerror = function(error) {
    console.error("EventSource failed: ", error);
};
  • new EventSource("/sync/stream"): 创建一个连接到指定 URL 的 EventSource 实例。
  • onmessage: 每当服务器发送一个事件时,会触发该事件。
  • onerror: 当发生错误时,会触发该事件。

4. 扩展应用场景

4.1 实时任务进度推送

可以利用 SseEmitter 来推送后台任务的实时进度。

例如,在执行一个需要时间的任务时,可以定期向客户端推送进度更新。

@GetMapping("/task/progress")
public SseEmitter streamTaskProgress() {
    SseEmitter emitter = new SseEmitter();
    new Thread(() -> {
        try {
            for (int i = 0; i <= 100; i++) {
                emitter.send("data: " + i + "% complete\n\n");
                Thread.sleep(1000);  // 模拟任务处理
            }
            emitter.complete();  // 任务完成,结束 SSE 流
        } catch (Exception e) {
            emitter.completeWithError(e);  // 出现异常时结束流
        }
    }).start();
    return emitter;
}

4.2 实时聊天系统

在实时聊天应用中,可以使用 SSE 向所有连接的客户端推送消息。

@GetMapping("/chat/{roomId}")
public SseEmitter streamChatMessages(@PathVariable String roomId) {
    SseEmitter emitter = new SseEmitter();
    chatService.addListener(roomId, message -> {
        try {
            emitter.send("data: " + message + "\n\n");
        } catch (IOException e) {
            emitter.completeWithError(e);
        }
    });
    return emitter;
}

在这个例子中,

chatService.addListener(roomId, message -> {...}) 

是监听指定聊天室消息的逻辑,所有聊天信息都会通过 SseEmitter.send() 推送到客户端。

4.3 动态数据显示

SSE 适用于动态显示数据更新的场景,比如股票价格、天气预报等实时数据。

@GetMapping("/live-stock/{symbol}")
public SseEmitter streamStockPrice(@PathVariable String symbol) {
    SseEmitter emitter = new SseEmitter();
    stockService.addPriceListener(symbol, price -> {
        try {
            emitter.send("data: " + price + "\n\n");
        } catch (IOException e) {
            emitter.completeWithError(e);
        }
    });
    return emitter;
}

在这个例子中,

stockService.addPriceListener(symbol, price -> {...})

监听股票的实时价格变化,并通过 SseEmitter.send() 将最新价格推送到客户端。

5. 总结

Spring 的 SseEmitter 提供了一种简洁且高效的方式来实现服务器向客户端推送实时事件。通过 SSE,我们可以轻松地在后台执行长时间任务,并将实时数据推送给前端应用。

SSE 适用于许多场景,如任务进度推送、实时消息通知、动态数据展示等。相较于 WebSocket,SSE 实现简单,且不需要额外的协议支持,适合轻量级的实时应用。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。

相关文章

  • SpringBoot QQ邮箱发送邮件实例代码

    SpringBoot QQ邮箱发送邮件实例代码

    大家好,本篇文章主要讲的是SpringBoot QQ邮箱发送邮件实例代码,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • Java 数据结构与算法系列精讲之排序算法

    Java 数据结构与算法系列精讲之排序算法

    排序算法是《数据结构与算法》中最基本的算法之一。排序算法可以分为内部排序和外部排序,内部排序是数据记录在内存中进行排序,而外部排序是因排序的数据很大,一次不能容纳全部的排序记录,在排序过程中需要访问外存
    2022-02-02
  • 养成良好java代码编码规范

    养成良好java代码编码规范

    这篇文章主要介绍了如何养成良好java代码编码规范,规范需要平时编码过程中注意,是一个慢慢养成的好习惯,下面小编就带大家来一起详细了解一下吧
    2019-06-06
  • 解决idea每次新建项目都需要重新指定maven目录

    解决idea每次新建项目都需要重新指定maven目录

    这篇文章主要介绍了解决idea每次新建项目都需要配置maven,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • SpringBoot如何实现一个实时更新的进度条的示例代码

    SpringBoot如何实现一个实时更新的进度条的示例代码

    本文详细的介绍了SpringBoot如何实现一个实时更新的进度条,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-05-05
  • SpringBoot中配置文件及切换方式

    SpringBoot中配置文件及切换方式

    这篇文章主要介绍了SpringBoot中配置文件及切换方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • java基于jedisLock—redis分布式锁实现示例代码

    java基于jedisLock—redis分布式锁实现示例代码

    这篇文章主要介绍了jedisLock—redis分布式锁实现示例代码,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-11-11
  • Java实现Token工具类进行登录和拦截

    Java实现Token工具类进行登录和拦截

    在应用的登录时需要生成token进行验证,并放入信息,之后的话可以直接使用浏览器的session进行登录,本文就来利用java编写一个token工具类,可以很方便的生成和解析token,感兴趣的可以了解下
    2023-12-12
  • 如何解决Gradle、Maven项目build后没有mybatis的mapper.xml文件的问题

    如何解决Gradle、Maven项目build后没有mybatis的mapper.xml文件的问题

    这篇文章主要介绍了如何解决Gradle、Maven项目build后没有mybatis的mapper.xml文件的问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-01-01
  • java秒杀之redis限流操作详解

    java秒杀之redis限流操作详解

    这篇文章主要为大家详细介绍了java秒杀之redis限流操作,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11

最新评论