明天的太阳

导航

初识SSE

Event Stream WebSocket
基于HTTP协议 独立协议
轻量 简单 较为复杂
默认支持断线重连 自己实现
传输文本 传输二进制

HTTP请求、响应头

Content-Type: text/event-stream

数据格式

[field]:value\n
每行数据的格式都是[field]:value\n,数据以\n\n结束。
其中field可选项为: 空、id、event、data、retry。

Field可选项介绍

Field为空

:xxx\n
这时客户端要忽略消息内容,可以看作一种心跳机制。

Field为data

data:this is data\n\n

data:data1\n
data:data2\n\n

data表示为数据,一段消息内可以出现多个data。

Field为id

id:messagexxx\n
data:messageyyy\n\n

id为数据的唯一编号,浏览器会保存在lastEventId属性中,重连的时候会通过Last-event-ID请求头告知服务器消费位置。

Field为event

event:event1
data:dataxxx\n\n

data:dataxxx\n
data:datayy\n\n

event对应事件名称,可以根据事件名称来订阅事件。
event对应的值为空时,默认为message

Field为retry

retry:1000\n
data:xxxxxxx\n\n

指定浏览器重新发起链接的时间间隔,即断开连接后,等待该时间后再重连。

总结

除data外,所有Field都是可选的,data可以出现多次,其余的最多出现一次。

示例

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>SSE Demo</title>
</head>
<body>
<h1>SSE Demo</h1>
<div id="messages"></div>

<script>
    if (!!window.EventSource) {
        const source = new EventSource("/sse");
        source.onmessage = function(event) {
            const message = document.getElementById('messages');
            message.innerHTML += event.data + "<br>";
        };
        source.onerror = function(error) {
            console.log('EventSource failed:', error);
        };

        source.addEventListener("custom", event => {
            const message = document.getElementById('messages');
            message.innerHTML += event.data + "<br>";
        });

    } else {
        document.getElementById('messages').innerHTML = "Your browser doesn't support SSE!";
    }
</script>
</body>
</html>
@RestController
public class SseController {
    private ExecutorService executor = Executors.newSingleThreadExecutor();

    @GetMapping(value = "/sse", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public SseEmitter stream(@RequestHeader(name = "last-event-id", required = false) String lastEventId) {
        System.out.println(lastEventId);
        SseEmitter emitter = new SseEmitter();

        final int continueI = lastEventId == null ? 0 : Integer.parseInt(
                lastEventId.substring(lastEventId.lastIndexOf(":") + 2)
        );

        executor.submit(() -> {
            try {
                for (int i = continueI; true; i++) {
                    SseEmitter.SseEventBuilder eb = null;
                    if (i % 3 == 0) {
                        eb = SseEmitter.event()
                                .id(i+"")
                                .data("SSE message " + i)
                                .reconnectTime(10000)
                                .name("message");
                    } else {
                        eb = SseEmitter.event()
                                .id(i+"")
                                .data("custom message " + i)
                                .reconnectTime(10000)
                                .name("custom");
                    }
                    emitter.send(eb);
                    Thread.sleep(3000);
                }
            } catch (Exception e) {
                System.out.println(e.getMessage());
                emitter.completeWithError(e);
            }
        });

        return emitter;
    }
}

posted on 2024-02-21 23:41  东方来客  阅读(47)  评论(0编辑  收藏  举报