一、什么是 Server-Sent Events?
。客户端通过监听这些事件流,可以在接收到新事件时执行相应的操作。SSE 的一大优势在于它建立在标准的 HTTP 协议之上,不需要额外的握手和连接管理。
SSE 特点
- 简单性:SSE构建在HTTP协议之上,这意味着您无需引入额外的库或协议就可以开始使用。对于那些希望保持代码库干净并避免复杂性的开发人员来说,这是一个巨大的优势。
- 单向通信:SSE是一种单向通信协议,意味着数据只能从服务器流向客户端。这种单向性适用于各种情况,例如实时新闻更新、股票价格变动等,这些场景中,只有服务器需要推送数据。
- 自动重连:SSE连接在意外断开时会自动尝试重新连接。这种机制确保了在网络故障或连接中断后能够及时恢复通信,为用户提供连续的数据流。
- 事件流:SSE使用"事件流"(event stream)将数据从服务器发送到客户端。每个事件都可以包含一个事件标识符、事件类型和数据字段。客户端可以根据这些信息来解析和处理接收到的数据。
二、实现 Server-Sent Events
要实现 Server-Sent Events,我们需要创建一个 HTTP 服务器,负责向客户端推送事件流。以下是一个使用 Node.js 和 Next.js 框架的简单示例:
// pages/api/sse.js import { nanoid } from 'nanoid' export default function GET(req, res) { res.writeHead(200, { 'Content-Type': 'text/event-stream; charset=utf-8', 'Cache-Control': 'no-cache', Connection: 'keep-alive', }) // res.status(200).json({ text: 'Hello' }) res.write( `data: ${JSON.stringify({ id: 1, message: 'Hello, world!', timestamp: new Date().toISOString(), })} id: 1 ` ) res.write(`event: update data: bye-bye id: ${nanoid(6)} `) res.write('event: hi data: hi id: 100 ') let n = 0 let t = setInterval(() => { n++ if (n > 5) { clearInterval(t) res.end('event: end data: 结束通信。 id: 9999 ') } else { let id = nanoid(6) let data = JSON.stringify({ id, message: `hi,这是第 ${n} 条消息。`, timestamp: new Date().toISOString(), }) res.write(`data: ${data} id: ${id} `) } }, 1000) }
在这个示例中,我们创建了一个
三、在客户端使用 Server-Sent Events
客户端可以使用 JavaScript 中的
// pages/sse/index.jsx import { useReactive } from 'ahooks' import { Button, List, Space, Typography } from 'antd' import { useRef } from 'react' export const config = { maxDuration: 30, } export default function Page() { const sse = useRef() const that = useReactive({ list: [], }) function connect() { init() } function close() { sse.current?.close() } function init() { sse.current = new EventSource('/api/sse', {withCredentials: true}) // 连接一旦建立,就会触发open事件,可以在onopen属性定义回调函数。 sse.current.onopen = function (event) { // console.log(event) // console.log(sse.current.CONNECTING) // console.log(sse.current.OPEN) // console.log(event.lastEventId) } // 客户端收到服务器发来的数据,就会触发message事件, // 可以在onmessage属性的回调函数。 sse.current.onmessage = function (event) { // console.log('onmessage:') let {lastEventId, data, type} = event console.log({lastEventId, data, type}) that.list.push({lastEventId, data}) } sse.current.addEventListener('update', function (event) { // console.log('onupdate:') let {lastEventId, data, type} = event console.log({lastEventId, data, type}) }) sse.current.addEventListener('hi', function (event) { // console.log('onhi:') let {lastEventId, data, type} = event console.log({lastEventId, data, type}) }) sse.current.addEventListener('end', function (event) { // console.log('onhi:') let {lastEventId, data, type} = event console.log({lastEventId, data, type}) close() }) // 如果发生通信错误(比如连接中断),就会触发error事件, // 可以在onerror属性定义回调函数。 sse.current.onerror = function (event) { // handle error event } } function clear() { that.list = [] } return ( <div className="w-100% flex flex-col justify-center items-center"> <Space size="middle" style={{display: 'flex'}}> <Button onClick={close} danger> 断开连接 </Button> <Button onClick={connect} type="primary"> 建立连接 </Button> </Space> <br/> <List header={ <div className="flex justify-between"> 通信结果 <Button size="small" onClick={clear}> 清空 </Button> </div> } footer={null} bordered style={{width: '100%'}} dataSource={that.list} renderItem={({lastEventId, data}) => ( <List.Item> <Typography.Text mark>[{lastEventId || 0}]</Typography.Text> {data} </List.Item> )} /> </div> ) }
在这个示例中,我们使用
通过这样的简单实现,你可以在应用程序中实现实时通信,为用户提供更加即时和动态的体验。 Server-Sent Events 是一个强大而简单的工具,适用于多种场景,如实时聊天、通知推送等。
参考文档:
- https://developer.mozilla.org/zh-CN/docs/Web/API/EventSource
- https://mp.weixin.qq.com/s/7oaRLZD9p343bZLRynCcKA
- https://zh.javascript.info/server-sent-events#wan-zheng-shi-li
- https://mp.weixin.qq.com/s/7oaRLZD9p343bZLRynCcKA
欢迎访问:天问博客