springboot+sse的实现案例

一 sse

1.1 介绍

SSE(Server-Sent Events,服务器发送事件)是一种基于 HTTP 的服务器向客户端单向推送实时数据 的技术,属于 HTML5 标准的一部分。它允许服务器主动向客户端发送数据流,适用于低延迟、单向实时更新的场景(如新闻推送、实时监控、日志流展示等)。

1和websocket对比

2.输出的数据格式:

1.2 遇到的问题案例

1.报错代码

SseEmitter emitter = new SseEmitter(300_000L*20)

emitter.send(SseEmitter.event().data(message));

2.nginx这样配置

location /bocai-chat {

proxy_send_timeout 3600s;

proxy_connect_timeout 1200s;

proxy_read_timeout 3600s;

proxy_buffering off;

proxy_pass http://bocaicluster/bocai-chat;

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

}

3.这段代码提示报

org.springframework.web.context.request.async.AsyncRequestNotUsableException: ServletOutputStream failed to flush: java.io.IOException: Connection reset by peer

4.前端页面通过nginx代理访问且并没有刷新关闭页面,通过nginx代理后的页面访问报这个错误,直接绕过nginx返回后端服务程序是正常。最后排查的是nginx代理的后端负载服务器存在问题。

二 sse的案例

2.1 两种情况的说明

2.2 同步阻塞-发送数据

说明:同步阻塞:能发送数据,但无法触发onCompletion回调的执行。

因为同步阻塞,发送send,complete方法后,emitter.onCompletion()还没有注册。无法触发。

同时如果浏览器关闭,send发送也不会触发异常。

1.代码

复制代码
 /**
     * @author admin
     * @description      同步阻塞
     * @date 2025/12/20 12:39
     * @param []
     * @return org.springframework.web.servlet.mvc.method.annotation.SseEmitter
     */
    @GetMapping("/stream3")
    public SseEmitter streamDataCallBack() {
        SseEmitter emitter = new SseEmitter(300000L); // 0表示不超时
        // 设置完成和超时回调
        emitter.onCompletion(() -> System.out.println("完成.."));
        emitter.onTimeout(() -> {
                    System.out.println("超时...");
                    emitter.complete(); // 超时后主动完成
                }
        );
        try {
            Thread.sleep(10000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
            // 发送初始连接成功消息
            try {
                emitter.send(SseEmitter.event()
                        .name("CONNECTED")
                        .data("{\"status\": \"connected\", \"timestamp\": \"" +
                                System.currentTimeMillis() + "\"}"));
//              .data("你好当前是:"+System.currentTimeMillis()));
            } catch (Exception e) {
                System.out.println("报异常了:"+e);
                emitter.completeWithError(e);
            }
        try {
            Thread.sleep(30000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        System.out.println("正常....");
            emitter.complete(); // 此时回调已注册,会触发 onCompletion
        return emitter;
    }

2.效果图

控制台:

2.3 异步非阻塞-发送数据

1.能够触发,oncompletion的方法。加了异步处理

复制代码
 /**
    * @author admin
    * @description       正常场景
    * @date 2025/12/20 12:39
    * @param []
    * @return org.springframework.web.servlet.mvc.method.annotation.SseEmitter
    */
    @GetMapping("/stream")
    public SseEmitter streamData() {
        SseEmitter emitter = new SseEmitter(0L); // 0表示不超时

        // 将新的emitter添加到集合中
       // emitters.add(emitter);

        // 设置完成和超时回调
        emitter.onCompletion(() -> System.out.println("完成.."));
        emitter.onTimeout(() -> {
                    System.out.println("超时...");
                    emitter.complete(); // 超时后主动完成
                }
        );
        executor.schedule(() -> {
        // 发送初始连接成功消息
        try {
            emitter.send(SseEmitter.event()
                    .name("CONNECTED")
                    .data("{\"status\": \"connected\", \"timestamp\": \"" +
                            System.currentTimeMillis() + "\"}"));
//              .data("你好当前是:"+System.currentTimeMillis()));
        } catch (IOException e) {
            System.out.println("报异常了:"+e);
            emitter.completeWithError(e);
        }
            System.out.println("正常....");
            emitter.complete(); // 此时回调已注册,会触发 onCompletion
        }, 50, TimeUnit.SECONDS);
//       emitter.complete();
        return emitter;
    }

2.效果

2.4 异步非阻塞-正常发送数据-关闭浏览器

1.在浏览器输入地址访问:大概10s后,关闭浏览器

2.后端设置了延迟50s,在50s后开始执行,发现客户端已经断开,报错

2.5 异步非阻塞-超时

1.设置超时小一点,业务执行长一点;200毫秒肯定小于1秒

2.执行效果

控制台:看到触发超时回调方法

相关推荐
程序员鱼皮2 小时前
从夯到拉,锐评 28 个后端技术!
后端·计算机·程序员·开发·编程经验
05大叔2 小时前
多线程的学习
java·开发语言·学习
开心猴爷2 小时前
混合开发的 App 怎么测试?
后端
小萌新上大分2 小时前
synchronized的8锁问题(区分默认用的是那把锁) 笔记云备份
java·java多线程·synchronized·synchronized关键字·synchronized用法·多线程买票问题·java锁机制
俞凡2 小时前
eBPF + OpenTelemetry:适用于任何应用的零代码自动化测量
后端
Cache技术分享3 小时前
275. Java Stream API - flatMap 操作:展开一对多的关系,拉平你的流!
前端·后端
sino爱学习3 小时前
别再踩 Stream 的坑了!Java 函数式编程安全指南
java·后端
Sunsets_Red3 小时前
2025 FZYZ夏令营游记
java·c语言·c++·python·算法·c#