使用Java和Spring Boot实现服务器发送事件(Server-Sent Events)

引言

使用Java开发web应用,大多数时候我们提供的接口返回数据都是一次性完整返回。有些时候,我们也需要提供流式接口持续写出数据,以下提供一种简单的方式。

SSE(Server-Sent Events)

SSE 是一种允许服务器单向发送事件到客户端的技术,它基于HTTP协议,服务器可以推送消息到客户端,但客户端不能向服务器发送消息。

实现

直接上代码

java 复制代码
@RestController
public class TestController {

    private static final Logger logger = LoggerFactory.getLogger(TestController.class);

    @GetMapping("/test")
    public void test(HttpServletResponse response) {
        response.setContentType("text/event-stream");
        response.setCharacterEncoding("utf-8");

        try (final PrintWriter writer = response.getWriter()) {
            // 要推送的内容
            final String content = "你好,我的朋友,快过年了,提前祝你新年快乐!";
            int len = content.length();
            int endIndex = 0;
            // 每隔2个字符推送一次,模拟打字机效果
            while (endIndex < len) {
                endIndex = Math.min(endIndex + 2, len);
                final String subContent = content.substring(0, endIndex);
                // 将要推送的内容封装成JSON格式,模拟实际开发中的数据格式,非必须
                final JSONObject json = new JSONObject();
                json.put("data", subContent);
                json.put("code", HttpStatus.OK.value());
                // 最后一次推送时,type为finish,表示推送结束,其它情况为add
                final String type = endIndex == len
                        ? "finish"
                        : "add";
                json.put("type", type);
                // 组装成SSE格式的数据,发送给前端,这个格式(data: content\n\n)是固定的,content是自定义的推送内容
                writer.write("data: " + json.toJSONString() + "\n\n");
                writer.flush();
                // 稍微给点停顿,防止数据发送太快,浏览器接收不过来
                TimeUnit.MILLISECONDS.sleep(100);
            }
        } catch (Exception e) {
            Thread.currentThread().interrupt();
            logger.error("流式推送数据异常", e);
        }
    }
}

组装数据,格式固定为"data: " + content + "\n\n",这里的数据格式是服务器发送事件(Server-Sent Events,SSE)的标准格式。

SSE 中,数据必须以 "data: " 开头,然后是要发送的数据,最后是两个换行符"\n\n"。这是SSE的规定格式,客户端会根据这种格式来解析服务器发送的事件。

接口调试

用接口调试工具(我用的是Apifox)调试接口如下:

接口符合SSE规范,所以可以被正常识别为事件流推送。

SSE与其他实时通信技术的比较

SSE与WebSocket的比较

  • 通信方式:SSE是单向的,只能由服务器向客户端发送数据;而WebSocket是双向的,服务器和客户端都可以发送数据。
  • 协议:SSE基于HTTP协议,更易于设置和配置;WebSocket是一个独立的协议。
  • 数据格式:SSE发送的数据格式固定,必须是"text/event-stream";而WebSocket可以发送任何类型的数据。
  • 连接:SSE在断开连接后可以自动重新连接,而WebSocket需要手动处理重连。
  • 浏览器支持:WebSocket的浏览器支持更广泛,几乎所有现代浏览器都支持WebSocket;而SSE在某些旧版本的浏览器(如IE)中不被支持。

SSE与长轮询的比较

  • 效率:SSE更高效,因为它只需要一个HTTP连接,就可以持续地发送数据;而长轮询需要不断地建立和断开HTTP连接。
  • 实时性:SSE的实时性更强,因为服务器可以随时发送数据;而长轮询需要客户端不断地发送请求来获取新数据。
  • 复杂性:SSE的实现相对简单,只需要服务器按照规定的格式发送数据即可;而长轮询的实现较复杂,需要处理连接的建立和断开,以及错误和超时等问题。
  • 浏览器支持:与WebSocket相比,SSE和长轮询的浏览器支持都较差,但长轮询在更多的浏览器中被支持。
  • 适用场景:SSE适用于服务器需要主动推送数据的场景;而长轮询适用于客户端需要定期获取新数据,但服务器不需要主动推送数据的场景。

注意事项

  • 以上一个简单的事件流推送接口就实现好了,但是它的问题是客户端没法干预服务器的推流,如果需要中途停止接收内容,基于以上接口是没法做到的。所以实际项目开发过程中,可以使用Spring框架提供的SseEmitter,具体用法可以参考 轻松实现服务器事件推送!Spring SseEmitter 详解
  • 浏览器支持:并非所有浏览器都支持SSE,例如,旧版本的Internet Explorer就不支持SSE。在使用SSE时,需要确保目标用户的浏览器支持这项技术。
  • 连接限制:由于SSE需要保持长连接,因此可能会占用大量的服务器资源。在使用SSE时,需要考虑到这一点,并根据实际情况进行优化。
相关推荐
cainiao08060512 分钟前
Java 大视界:基于 Java 的大数据可视化在智慧城市能源消耗动态监测与优化决策中的应用(2025 实战全景)
java
Q_Q51100828527 分钟前
python的婚纱影楼管理系统
开发语言·spring boot·python·django·flask·node.js·php
长风破浪会有时呀40 分钟前
记一次接口优化历程 CountDownLatch
java
云朵大王1 小时前
SQL 视图与事务知识点详解及练习题
java·大数据·数据库
我爱Jack1 小时前
深入解析 LinkedList
java·开发语言
一线大码2 小时前
Gradle 高级篇之构建多模块项目的方法
spring boot·gradle·intellij idea
27669582923 小时前
tiktok 弹幕 逆向分析
java·python·tiktok·tiktok弹幕·tiktok弹幕逆向分析·a-bogus·x-gnarly
用户40315986396633 小时前
多窗口事件分发系统
java·算法
用户40315986396633 小时前
ARP 缓存与报文转发模拟
java·算法
小林ixn3 小时前
大一新手小白跟黑马学习的第一个图形化项目:拼图小游戏(java)
java