SSE从服务器单向向浏览器传输数据

611 阅读2分钟

是什么

SSE是Server-Sent Events的缩写。它是一种浏览器的技术,允许网站推送实时更新到浏览器,而无需浏览器发送请求。这种技术通常用于实现实时数据更新,例如新闻滚动条或社交媒体时间线。它可以帮助网站提供更加交互式和实时的体验。

发送的是消息流,也就是说一个连接被长时间的占用;一旦浏览器请求了一个接口,那么服务器就会主动向浏览器端发送信息。

只要浏览器支持,就可以使用。

优点

和WebSocket做对比。

  • SSE 使用 HTTP 协议,现有的服务器软件都支持。WebSocket 是一个独立协议。
  • SSE 属于轻量级,使用简单;WebSocket 协议相对复杂。
  • SSE 默认支持断线重连,WebSocket 需要自己实现。
  • SSE 一般只用来传送文本,二进制数据需要编码后传送,WebSocket 默认支持传送二进制数据。
  • SSE 支持自定义发送的消息类型。

实现

数据格式

http的请求头是

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

java API

1、在@RequestMapping中指定参数 produces = MediaType.TEXT_EVENT_STREAM_VALUE

2、创建一个SseEmitterSseEmitter emitter = new SseEmitter(300000L);,这个是可以指定超时时间的,如果你不指定的话会变成http连接的过期时间,很有可能你的数据还没有发完,然后这个http连接就断了(可以拿下面的代码试一下,将里面的参数去掉);

在发送数据的时候需要使用另一个线程去执行这个数据的传输,SseEmitter.send(Object object);

SseEmitter.event() .reconnectTime(1000) .id(String.valueOf(i)) .data(WORDS[i])
  • reconnectTime:指定浏览器重新发起连接的时间间隔。两种情况会导致浏览器重新发起连接:一种是时间间隔到期,二是由于网络错误等原因,导致连接出错。
  • id:数据片段id;
  • data:发送的数据体;

例子

@Controller
public class SSEController {
    private static final Logger logger = LoggerFactory.getLogger(SSEController.class);

    private static final String[] WORDS = "The quick brown fox jumps over the lazy dog.".split(" ");

    private final ExecutorService cachedThreadPool = Executors.newCachedThreadPool();

    @GetMapping(path = "/words", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    SseEmitter getWords(@RequestHeader(name = "Last-Event-ID", required = false) String lastId) {
        logger.info("Id of last received event: {}", lastId);

            
        SseEmitter emitter = new SseEmitter(300000L); // 指定超时时间300000毫秒
        
        logger.info("Emitter created: {}", emitter);

        cachedThreadPool.execute(() -> {
            try {
                System.out.println(WORDS.length);
                for (int i = parseLastId(lastId); i < WORDS.length; i++) {
                    emitter.send(
                            SseEmitter.event()
                                    .reconnectTime(1000)
                                    .id(String.valueOf(i))
                                    .data(WORDS[i])
                    );
                    
                    TimeUnit.SECONDS.sleep(5);
                }

                emitter.complete();
                logger.info("Emitter completed: {}", emitter);
            } catch (Exception e) {
                emitter.completeWithError(e);
                logger.error("Emitter failed: {}", emitter, e);
            }
        });

        return emitter;
    }

    private int parseLastId(String lastId) {
        try {
            return Integer.parseInt(lastId) + 1;
        } catch (NumberFormatException e) {
            return 0;
        }
    }

}