Server-Sent Events(服务器发送事件 - 即时通讯技术)单向推送实时数据

引言

        本文以即时通讯技术的发展历程介绍相关技术以及实现原理,深入探讨其中Server-Sent Events技术的原理、应用场景以及实现方法

即时通讯技术发展历程

HTML标准

  1. 短轮询:客户端定时向服务器发送HTTP请求,无论有无最新数据都会立即返回响应,这种方式存在很多无效请求,实时性差,服务器负载高
  2. 长轮询:客户端向服务器发送HTTP请求,服务器会保持这个连接为打开状态,直到有新数据可以发送或连接超时才会返回响应并关闭连接,客户端立即再次发送新的HTTP请求,这种方式需要服务器支持长连接和超时处理,消耗更多资源
  3. Comet:客户端向服务器发送HTTP请求,服务器会保持这个连接为打开状态,直到有新数据可以发送或连接超时才会返回响应,不会关闭连接,这种方式需要保持大量连接并等待数据更新,消耗更多资源,连接不稳定会导致数据传输终端中断

HTML5标准

  • WebSocket:在单个TCP连接上进行全双工通信的协议,允许服务端主动向客户端推送数据,浏览器和服务器只需要完成一次握手,两者之间就可以创建持久性的连接,进行双向数据传输
  • Server-Sent Events:基于HTTP协议的轻量级推送技术,在客户端与服务器之间建立一条持久化连接,进行单向数据传输,服务器主动向客户端推送事件流数据,定义了一种新的MIME类型text/event-stream,用于标识服务器发送的事件流数据

应用场景

        当我们需要双向数据传输时只能选择WebSocket,如果是服务器向客户端单向数据传输时,WebSocket和Server-Sent Events都能实现,优先Server-Sent Events

工作原理

  1. 建立连接:客户端通过创建一个EventSource对象并指定URL来向服务器发送SSE请求,这个请求时一个普通的HTTP GET请求,服务器会保持连接为打开状态
  2. 服务器响应:服务器收到SSE请求后,标识出这是一个SSE连接,并设置响应头中的Content-Type为text/event-stream
  3. 事件发送:服务器通过已建立的HTTP长连接向客户端发送事件,服务器可以连续发送多个事件,客户端会按顺序接收并处理这些事件
  4. 客户端接收:客户端的EventSource对象监听open、message和error等事件,当连接建立时,会触发open事件,当接收到新的事件时,会触发message事件,并将事件数据作为参数传递给事件处理函数,如果连接断开或出现错误,会触发error事件

示例代码

工程结构

后端

/**
 * Server-Sent Events 控制层
 * @author muze
 */
@Slf4j
@RestController
@RequestMapping("/sse")
public class SseController {
    private final SseEmitter sseEmitter = new SseEmitter(Long.MAX_VALUE);
    /**
     * 建立事件流
     * produces 指定响应的内容类型为 SSE 所需的文本事件流格式
     * @param eventStreamName 事件流名称
     * @return SseEmitter实例
     */
    @GetMapping(value = "/createEventStream/{eventStreamName}", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
    public ResponseEntity<SseEmitter> dataPush(@PathVariable("eventStreamName") String eventStreamName) {
        try {
            sseEmitter.send(SseEmitter.event().name(eventStreamName).data("建立事件流 [" + eventStreamName + "] 成功"));
        } catch (IOException e) {
            log.error("建立事件流 [{}] 失败", eventStreamName);
        }
        return ResponseEntity.ok().body(sseEmitter);
    }

    /**
     * 发送消息
     * @param messageDTO 消息DTO对象
     */
    @PostMapping("/sendMessage")
    public void sendMessage(@RequestBody MessageDTO messageDTO) {
        try {
            sseEmitter.send(SseEmitter.event().name(messageDTO.getEventStreamName()).data(messageDTO.getMessage()));
        } catch (IOException e) {
            log.error("消息发送失败:{}", e.getMessage());
        }
    }
}

前端

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>SSE Demo</title>
</head>

<body>
    <h1>Server-Sent Events Demo</h1>
    <div id="demo"></div>

    <script>
        // 检查浏览器是否支持 EventSource 接口
        if (!!window.EventSource) {
            // 创建一个 EventSource 对象,该对象连接到服务器的 /sse/demo 端点以接收事件
            const source = new EventSource('/sse/createEventStream/demo');

            // 给名为 demo 的事件添加事件监听器
            source.addEventListener('demo', function (response) {
                document.getElementById('demo').innerHTML += '<p>' + response.data + '</p>';
            });

            // EventSource 对象异常时,打印错误信息并关闭EventSource 对象
            source.onerror = function (response) {
                console.error('EventSource failed:', response);
                source.close();
            };
        } else {
            document.getElementById('demo').innerHTML = '<p>Your browser does not support Server-Sent Events.</p>';
        }
    </script>
</body>

</html>

运行效果

启动项目访问:http://localhost:8080/index.html

使用接口测试工具访问:http://localhost:8080/sse/sendMessage

        至此一个简单的使用Server-Sent Events实现服务器向客户端单向传输数据的示例代码就完成了,在此基础上你可以根据自己的需求扩展工程,可能业务上还会用到策略模式,此时你还可以看看我的另一篇文章Java设计模式系列 - 策略模式:灵活选择算法的行为艺术-CSDN博客,赶快去实际操作一下Server-Sent Events吧,后续我还会写一篇有关WebSocket的文章,希望这篇文章对你有所帮助!

  • 25
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值