SSE(Server-Sent Events)请求之EventSource

文章介绍了Server-SentEvents(SSE)的概念、特点和如何在搜索接口中实现流式返回,以及EventSource的使用方法。对比了SSE与WebSocket在双向通信和应用场景上的区别。

前言

最近有个需求有个搜索接口需要支持流式和静态两种方法,这个接口的查询时间可能很长,完全等结果出来再返回前端对于用户体验不好,于是就想实现持续性返回以提高用户体验。于是学习一下SSE。

什么是SSE

SSE 是指 Server-Sent Events(服务器推送事件),是一种基于 HTTP 的、服务器向客户端单向推送数据的技术。它允许服务器实时发送数据到客户端,而不需要客户端明确地请求。SSE 主要用于实现服务器向客户端的实时更新,比如新闻更新、股票报价、实时监控等场景。
SSE 的优点在于其简单性和易用性,特别适用于那些需要服务器向客户端实时推送信息的场景。虽然它与 WebSocket 相比有一些限制(比如单向通信),但对于一些应用场景而言,SSE 是一个简单而有效的选择。

一些关键特点包括:

  • 单向通信: SSE 使得服务器可以推送实时数据到客户端,但并不支持客户端向服务器发送数据。

  • 基于标准的 HTTP/HTTPS: SSE 使用普通的 HTTP 或 HTTPS 协议。它与 WebSocket 不同,不需要专门的协议,可以通过常规的网络端口进行通信。

  • 事件流: 服务器发送的数据是一系列事件流,每个事件都以一行文本的形式发送,使用特定的格式和字段(如 eventdataid 等),客户端接收到数据后可以解析并处理它们。

  • 自动重连: SSE 具有自动重连机制,当连接中断时,浏览器会尝试重新建立连接,确保持续地接收服务器的事件流。

什么是EventSource

EventSource 是用于接收服务器端发送的服务器推送事件(Server-Sent Events,SSE)的接口。它允许客户端从服务器端获取实时更新,是一种基于 HTTP 的单向通信机制。

创建 EventSource 实例时,可以传入以下参数:

  • URL(字符串): 指定服务器端点的 URL,从中接收 SSE 事件。
  • 配置对象(可选): 包含设置连接的属性,如请求头、withCredentials 等。

EventSource 方法和事件:

  • EventSource 实例属性:

    • onopen 事件连接建立时触发的回调函数。
    • onmessage 接收到来自服务器端的消息时触发的回调函数。
    • onerror 连接出错时触发的回调函数。
  • EventSource 实例方法:

    • close() 手动关闭 SSE 连接。当你想要终止连接时使用。
  • EventSource 实例 readyState 属性:

    • CONNECTING 表示连接正在建立。
    • OPEN 表示连接已经建立。
    • CLOSED 表示连接已经关闭。

创建 EventSource 实例的参数可以包括:

  • URL 字符串: 作为第一个参数传递,是必需的,用来指定发送 SSE 请求的服务器端点。
  • 配置对象(可选): 可以包含 headers(请求头)等属性,例如:
const eventSource = new EventSource('your_server_endpoint', {
    headers: {
        'Authorization': 'Bearer your_token'
        // 请求头其他信息
    },
    withCredentials: true // 是否发送凭据(比如Cookie)
});

eventSource.onopen = (event) => {
    // 连接建立时的操作
};

eventSource.onerror = (error) => {
    // 错误处理
};

eventSource.onmessage = (event) => {
    // 处理接收到的消息 event.data
};

使用示例

当使用 SSE 在前端实现搜索接口的流式展现时,可以创建一个事件流来接收来自服务器的数据,然后实时将数据展示在页面上。这里有一个简单的例子,假设有一个服务器端点 /search 用于接收搜索请求,并以 SSE 的方式返回搜索结果。

服务器端 SSE 设置(Node.js/Express 示例):

const express = require('express');
const app = express();

// 配置接口
app.get('/search', (req, res) => {
    res.writeHead(200, {
        'Content-Type': 'text/event-stream',
        'Cache-Control': 'no-cache',
        'Connection': 'keep-alive'
    });

    // 模拟搜索结果
    const searchResults = ['Result 1', 'Result 2', 'Result 3', 'Final Result'];

    searchResults.forEach((result, index) => {
        // 模拟延迟以进行演示
        setTimeout(() => {
            res.write(`data: ${result}\n\n`);
            if (index === searchResults.length - 1) {
                res.end(); // 发送所有结果后关闭连接
            }
        }, 2000 * index); // 模拟发送结果,延迟 2 秒

    });
});

app.listen(3000, () => {
    console.log('Server running on port 3000');
});

这是一个简单的 Express 服务器,当接收到 /search 路径的请求时,会以 SSE 的方式发送模拟的搜索结果。

前端实现:

const eventSource = new EventSource('/search');

// 监听消息
eventSource.onmessage = (event) => {
    const searchResults = document.getElementById('searchResults');
    searchResults.innerHTML += event.data + '<br>'; // 展示数据
};

// 处理错误
eventSource.onerror = (error) => {
    console.error('SSE error:', error);
};

这段前端代码会创建一个 SSE 连接,监听来自服务器的消息,并将它们展示在 ID 为 searchResults 的 HTML 元素中(假设有一个用于展示结果的 div 或其他元素)。

确保页面上有一个元素,比如 <div id="searchResults"></div> 用于展示搜索结果。

这是一个简单的例子,实际场景中需要根据自己的业务逻辑来处理搜索结果并相应地展示在页面上。

EventSource与WebSocket区别

EventSource 和 WebSocket 是两种用于实现实时通信的技术,但在功能和应用场景上有一些明显的区别。

EventSource:

  • 单向通信: EventSource 是服务器向客户端的单向通信,客户端接收来自服务器的事件流。
  • 基于HTTP协议: EventSource 基于标准的 HTTP/HTTPS 协议,使用长轮询或类似的机制,但并不是完全双向的通信。
  • 文本数据传输: 通常用于传输文本数据,如服务器推送的消息或事件。
  • 自动重连: 当连接中断,EventSource 会自动尝试重新连接,不需要手动处理重连。
  • 适用场景: 适用于服务器向客户端单向推送信息,比如实时更新的新闻、股票报价等,但不适合需要客户端与服务器进行双向通信的场景。

WebSocket:

  • 双向通信: WebSocket 提供全双工、双向通信的能力,客户端和服务器之间可以互相发送数据。
  • 独立的协议: WebSocket 使用自己的协议,建立在 TCP 连接之上,允许在同一个连接上进行双向通信。
  • 任意数据传输: 能够传输任意类型的数据,不限于文本,可以是二进制数据。
  • 手动处理重连: 当连接中断,需要在客户端代码中实现重新连接的逻辑。
  • 适用场景: 适用于需要双向实时通信的场景,比如聊天应用、在线游戏等,其中客户端和服务器都需要发送和接收数据。

EventSource 更适用于服务器向客户端的单向通信,而 WebSocket 则适合需要客户端和服务器双向实时通信的场景。选择使用哪种技术取决于你的具体需求和应用场景。

### 什么是 Server-Sent Events (SSE) Server-Sent Events (SSE) 是一种基于 HTTP 的技术,允许服务器向客户端推送实时更新的数据流。它是一种单向通信机制,适合用于需要从服务器到客户端持续传输数据的应用场景[^1]。 --- ### SSE 的主要特点 - **单向通信**:SSE 只支持从服务器到客户端的单向数据流,不支持双向通信。 - **低延迟**:相比轮询或其他传统方法,SSE 提供更低的延迟和更高的效率。 - **标准协议**:SSE 基于 HTTP 协议,兼容性强,易于实现。 - **事件驱动模型**:可以通过自定义事件名称来区分不同的消息类型。 --- ### SSE 的基本工作原理 当客户端请求一个支持 SSE 的资源时,服务器会保持连接并定期发送数据直到连接关闭或超时。每次发送数据时,服务器可以指定特定的消息格式和元信息,例如事件名称、重试时间等[^2]。 --- ### 实现 SSE 的关键要素 #### 客户端部分 在浏览器中,JavaScript 提供了一个 `EventSource` 对象,用于监听来自服务器的事件流。以下是创建 `EventSource` 并处理接收到的消息的基本语法: ```javascript const eventSource = new EventSource('/stream'); eventSource.onmessage = function(event) { console.log('New message:', event.data); }; eventSource.onerror = function(err) { console.error('Error occurred:', err); }; ``` #### 服务端部分 服务端需要返回带有特殊 MIME 类型 (`text/event-stream`) 的响应头,并按照 SSE 的格式发送数据。以下是一个通用的服务端响应结构: ```http HTTP/1.1 200 OK Content-Type: text/event-stream Cache-Control: no-cache Connection: keep-alive Access-Control-Allow-Origin: * ``` 每条消息应遵循如下格式: ``` id: <id>\n event: <event-name>\n data: <json-data>\n\n ``` 其中: - `id`: 消息唯一标识符(可选)。 - `event`: 自定义事件名(可选)。 - `data`: 要发送的实际数据。 --- ### 使用 Go 实现 SSE 示例 以下是在 Go 中实现 SSE 的简单示例代码: ```go package main import ( "fmt" "net/http" "time" ) func streamHandler(w http.ResponseWriter, r *http.Request) { w.Header().Set("Content-Type", "text/event-stream") w.Header().Set("Cache-Control", "no-cache") w.Header().Set("Connection", "keep-alive") for i := 0; ; i++ { fmt.Fprintf(w, "data: Message %d at %v\n\n", i, time.Now()) w.(http.Flusher).Flush() time.Sleep(1 * time.Second) } } func main() { http.HandleFunc("/stream", streamHandler) err := http.ListenAndServe(":8080", nil) if err != nil { panic(err) } } ``` 此代码启动了一个简单的 SSE 流服务,在 `/stream` 路径下不断发送带有时戳的消息。 --- ### 使用 Spring Boot 实现 SSE 示例 如果使用 Java 和 Spring Boot,则可通过控制器中的 `TextEventStream` 返回值轻松实现 SSE 功能。下面是一个完整的例子: ```java import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import java.time.LocalDateTime; @RestController public class SseController { @GetMapping(value = "/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE) public Flux<String> streamEvents() { return Flux.interval(Duration.ofSeconds(1)) .map(sequence -> LocalDateTime.now() + " : " + sequence); } } ``` 在此示例中,Spring Boot 利用了反应式编程库 `Flux` 来生成定时器触发的消息流[^2]。 --- ### 总结 无论是使用 Go 还是 Spring Boot,SSE 都能以较低复杂度实现实时数据推送的功能。对于只需要单向通信且不需要 WebSocket 复杂性的应用场景来说,SSE 是一个非常合适的选择。 ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值