前言
Server-Sent Events (SSE)是一种 HTML5 提供的实时通信技术,它允许浏览器端通过一种持续的连接,接收来自服务器端的实时事件推送。
在 Web 应用程序中,SSE 技术通常被用于实现实时通知、实时数据更新等功能。
本文将详细介绍如何使用 SSE 技术实现实时火车到站提示功能。
功能介绍
为了方便说明,我们以一个简化的功能需求为例:
用户在网站上输入火车站的名称,系统需要实时获取该火车站的即将到站的火车信息,并将其实时显示在页面上。
通常,这个功能需要满足以下要求:
- 实时性:系统需要实时获取到站信息,并实时显示在页面上。
- 数据量:每个火车站可能会有数百、数千条到站信息,需要处理大量的数据。
- 可靠性:服务器端需要保证数据传输的可靠性,以免数据丢失或重复发送。
下面,我们将一步步地实现这个实时火车到站提示功能。
技术方案
本文选择了以下技术方案:
- 服务器端:Node.js + Express + MongoDB
- 前端:HTML5 + CSS3 + JavaScript
- 客户端通信:Server-Sent Events
服务器端实现
MongoDB 数据库
我们需要准备一个 MongoDB 数据库,用来存储火车到站信息。
具体实现过程略。
Express 路由
我们需要在 Express 中定义一个路由,用来接收客户端的请求,并返回 SSE 实时数据流。
代码示例:
// javascriptcn.com 代码示例 const express = require('express'); const router = express.Router(); const Train = require('../models/train'); router.get('/train/:station', (req, res) => { const station = req.params.station; const stream = res .status(200) .set({ 'Content-Type': 'text/event-stream', 'Cache-Control': 'no-cache' }) .flushHeaders() .stream(); const findTrainStream = Train.find( { station, arriveTime: { $gte: new Date() } } ).sort({ arriveTime: 1 }).stream(); findTrainStream.on('data', async (doc) => { const message = `event: traindata\n` + `data: ${JSON.stringify(doc)}\n\n`; stream.write(message); }); findTrainStream.on('error', (err) => { console.error(err); stream.end(); }); findTrainStream.on('close', () => { console.log(`findTrainStream for ${station} is closed.`); stream.end(); }); }); module.exports = router;
上述代码中,我们定义了一个 GET /train/:station
的路由,用来接收客户端请求。
在这个路由中,我们首先获取客户端请求中指定的火车站名称 station
。
然后,我们设置了 Content-Type
和 Cache-Control
等 HTTP 头部信息,并通过 res.stream()
方法返回一个 SSE 实时数据流。
接下来,我们使用 Train.find()
函数从 MongoDB 中查询到站数据并返回一个数据流,在返回的数据流上,我们注册了三个事件处理器:
data
事件:当有新数据时,我们将该数据转换为 SSE 协议格式,并通过实时数据流向客户端推送数据。error
事件:当查询发生错误时,我们关闭实时数据流并打印日志。close
事件:当查询结束时,我们关闭实时数据流并打印日志。
整个过程中,我们使用了 Node.js 的 stream
模块来处理实时数据流。
后台服务
我们需要在 Node.js 中创建一个后台服务,用来监听客户端请求,并将请求转发到 Express 路由。
代码示例:
// javascriptcn.com 代码示例 const express = require('express'); const app = express(); const mongoose = require('mongoose'); const trainRouter = require('./routes/train'); mongoose.connect('mongodb://localhost/train', { useNewUrlParser: true }) .then(() => console.log('MongoDB connected')) .catch((err) => console.error(err)); app.use(express.json()); app.use(express.urlencoded({ extended: false })); app.use('/api', trainRouter); const PORT = process.env.PORT || 5000; app.listen(PORT, () => { console.log(`Server started on port ${PORT}`); });
上述代码中,我们先使用 mongoose.connect()
方法连接到 MongoDB 数据库。
然后,我们使用 app.use()
方法注册了一个 Express 路由。
最后,我们使用 app.listen()
方法启动了一个 HTTP 服务器。
前端实现
HTML5 页面
我们需要在 HTML5 页面中添加一个火车站输入框和一个实时到站信息列表。
代码示例:
// javascriptcn.com 代码示例 <!DOCTYPE html> <html> <head> <meta charset="UTF-8"> <title>实时火车到站提示</title> <style> /* 样式略 */ </style> </head> <body> <h1>实时火车到站提示</h1> <label for="station">请输入火车站名称:</label> <input type="text" id="station" name="station"> <br><br> <table id="trainTable"> <thead> <tr> <th>车次</th> <th>始发站</th> <th>终点站</th> <th>到达时间</th> </tr> </thead> <tbody></tbody> </table> <script src="/js/train.js"></script> </body> </html>
上述代码中,我们在 HTML 代码中定义了一个文本框(ID 为 station
)和一个数据列表(ID 为 trainTable
)。
CSS3 样式
我们需要在 CSS3 中定义一些样式,美化 HTML 页面。
代码示例:
/* 样式略 */
略。
JavaScript 脚本
我们需要编写 JavaScript 脚本,用来向服务器端请求实时数据,并将实时数据显示在页面上。
代码示例:
// javascriptcn.com 代码示例 const trainTable = document.querySelector('#trainTable'); const stationInput = document.querySelector('#station'); const source = new EventSource(`/api/train/${encodeURIComponent(stationInput.value)}`); source.addEventListener('open', (event) => { console.log(`EventSource connection opened.`); }); source.addEventListener('error', (event) => { console.error(`EventSource error: ${event}`); }); source.addEventListener('traindata', (event) => { const train = JSON.parse(event.data); const row = trainTable.insertRow(-1); const cell1 = row.insertCell(0); const cell2 = row.insertCell(1); const cell3 = row.insertCell(2); const cell4 = row.insertCell(3); cell1.innerHTML = train.trainNumber; cell2.innerHTML = train.startStation; cell3.innerHTML = train.arriveStation; cell4.innerHTML = new Date(train.arriveTime).toLocaleString(); }); stationInput.addEventListener('change', (event) => { const newSource = new EventSource(`/api/train/${encodeURIComponent(stationInput.value)}`); newSource.addEventListener('open', (event) => { console.log(`EventSource connection opened.`); }); newSource.addEventListener('error', (event) => { console.error(`EventSource error: ${event}`); }); newSource.addEventListener('traindata', (event) => { const train = JSON.parse(event.data); const row = trainTable.insertRow(-1); const cell1 = row.insertCell(0); const cell2 = row.insertCell(1); const cell3 = row.insertCell(2); const cell4 = row.insertCell(3); cell1.innerHTML = train.trainNumber; cell2.innerHTML = train.startStation; cell3.innerHTML = train.arriveStation; cell4.innerHTML = new Date(train.arriveTime).toLocaleString(); }); source.close(); source = newSource; });
上述代码中,我们通过 document.querySelector()
方法获取到文本框 stationInput
和数据列表 trainTable
。
然后,我们使用 new EventSource()
方法创建了一个 SSE 连接,并向服务器端发起请求。
在 SSE 连接中,我们注册了三个事件处理器:
open
事件:当连接成功时,我们在浏览器控制台中打印一条日志。error
事件:当连接出错时,我们在浏览器控制台中打印一条错误日志。traindata
事件:当有新数据到达时,我们将该数据添加到数据列表trainTable
中。
同时,我们通过 addEventListener()
方法注册了一个 change
事件处理器,用于处理文本框内容变化时,重新发起 SSE 连接并获取数据。
总结
本文介绍了如何使用 SSE 技术实现实时火车到站提示功能。
在本文中,我们选择了 Express + MongoDB 技术栈作为后端,选择了 HTML5 + CSS3 + JavaScript 技术栈作为前端。
在实现过程中,我们使用了 Node.js 的 stream
模块来处理实时数据流,并使用了 EventSource
对象在客户端与服务器之间建立 SSE 连接。
上述技术方案既可以满足实时性的要求,又可以处理大量的数据,同时具有较高的可靠性。
希望本文可以对读者在深入学习 SSE 技术方面提供一些指导意义。
示例代码
本文的完整示例代码已上传至 Github,欢迎有兴趣的读者进行研究。
https://github.com/littlesqx/sse-train-demo
来源:JavaScript中文网 ,转载请注明来源 本文地址:https://www.javascriptcn.com/post/6548644a7d4982a6eb2aa17d