SSE 是什么?
SSE 全称是 Server Sent Event,翻译过去的意思就是 服务器派发事件。
一个网页获取新的数据通常须要发送一个申请到服务器,也就是向服务器申请的页面。
应用 server-sent 事件,服务器能够在任何时刻向咱们的 Web 页面推送数据和信息。
这些被推送进来的信息能够在这个页面上作为 Events
[0]
+ data 的模式来解决。
文言篇
SSE 的实质其实就是一个 HTTP 的长连贯,只不过它给客户端发送的不是一次性的数据包,而是一个 stream 流,格局为 text/event-stream。
所以客户端不会敞开连贯,会始终等着服务器发过来的新的数据流
SSE 与其余交互方式的区别
SSE 与 WS 有什么区别?
形式 | 协定 | 交互通道 | 内容编码 | 重连 | 事件类型 | 总结 |
---|---|---|---|---|---|---|
SSE | HTTP | 服务端单向推送 | 默认文本 | 默认反对断线重连 | 反对自定义音讯类型 | 轻量级 |
WebSocket | WS(基于 TCP 传输层的应用层协定,RFC6455 [1] 对于它的定义规范) |
双向推送 | 默认二进制 | 手动实现 | NO | 扩展性、功能性弱小 |
TCP/IP 五层模型
SSE 兼容性
API | |||||
---|---|---|---|---|---|
SSE | 6.0 | 79.0 | 6.0 | 5.0 | 11.5 |
WS | 4.0 | 12 | 11 | 4.2 | 12.1 |
IE 不反对?兼容性不好?
event-source-polyfill 助你实现 SSE 梦
接下来的示例不会通过这个 event-source-polyfill 实现。
正经人谁思考兼容性?
狗头护体.jpg
SSE 的 API
属性(只读)
名称 | 作用 | 类型 | 备注 |
---|---|---|---|
readyState | 以后状态 | Number | 0 — connecting1 — open2 — closed |
url | 以后连贯的地址 | String | |
withCredentials | 是否开启凭据收集 | Boolean |
办法
名称 | 作用 | 返回值 |
---|---|---|
close | 客户端被动敞开连贯 | – |
事件
名称 | 作用 | 返回值 |
---|---|---|
onclose | 连贯敞开触发 | event |
onopen | 连贯开启触发 | event |
onmessage | 服务端音讯推动音讯触发 | event |
服务端 API
字段
名称 | 作用 | 类型 | 备注 |
---|---|---|---|
data | 传输的文本 | String(默认)\ 能够传输 JSON | 能够多行累加 |
event | 事件名称 | String | 可自定义 |
id | 以后推送 id | String | 作为音讯的标识 |
retry | 超时重试工夫 | Number | 客户端在感知 server 连贯异样后。会通过 retry 设定工夫进行从新连贯 |
需要评审
咱们在后面曾经初步的理解了 SSE,并且晓得了它的一些 API。
咱们接下来须要做一件事件,理解需要
需要就是
需要评审完结
产品经理给你一张图都不错了,本人看着来吧。
技术评审
后面的 ** 产品经理居然只给了一张图
用户在关上窗口时,会建设一个 SSE 的连贯
服务端须要将建设这次连贯,保留在连接池中。
如果用户点击了构建,后台程序会执行打包命令
在打包中进行音讯的推送,而后前端进行音讯的展现。
接口评审
GET api/log/push
(通信 API)
连贯 SSE 的通道,用于后续音讯通信
POST api/project/build
(构建 API)
参数 projectPath
传入你须要构建的我的项目门路
用于我的项目构建,构建时触发音讯推送
沉着一下
等等~ 是不是说得太简略了
构建 API 如何帮咱们打包?
nodejs 外面有个 child_process[3]
模块叫做 子过程
你能够通过它执行 install
、build
。
如何触发通信 API 音讯推送?
child_process
是能够异步推送音讯
外面会有 stdout
和 stderr
钩子。
会感知到命令行的 失常
和异样
输入。
咱们在这一步进行音讯的实时推送岂不美哉。
解答完这两个疑难之后,咱们就能够欢快的写代码了
后端开发
引入须要用到的包
这些包看起来引了挺多,实际上与 SSE 相干的 只有 koa-sse-stream,它其实就是一个他人封装好的中间件,如果你看过阮一峰老师的《Server-Sent Events 教程》[4]
const Koa = require('koa');
const router = require('koa-router')();
const KoaSSEStream = require('koa-sse-stream'); // 封装好的 SSE 中间件
const child_process = require('child_process'); // Node 子过程
const bodyParser = require('koa-bodyparser');
const cors = require('@koa/cors');
const moment = require('moment');
const newDate = () => moment().format('YYYY-MM-DD HH:mm:ss');
const app = new Koa();
app.use(cors());
app.use(bodyParser());
/*
接下来的代码存放处
*/
app.use(router.routes())
app.listen(3000);
通信 API
在后面咱们曾经说过每次减少一个连贯,会放到连接池中,而这个连接池相当于一个通讯录。
后续会遍历这个通讯录,进行音讯的推送
// 连接池
const clientList = [];
// koa-sse-stream 配置
const SSE_CONF = {
maxClients: 2, // 最大连接数
pingInterval: 40000 // 重连工夫
}
router.get('/api/log/push', KoaSSEStream(SSE_CONF), ctx => {
// 每次连贯会进行一个 push
clientList.push(ctx.sse);
})
构建 API
咱们在这个接口调用的时候,先进行响应,不然这个接口会始终等到构建实现之后再响应。
谁也不晓得构建实现某个我的项目,须要破费多长时间,可能设置的申请超时工夫都过了也没响应。
router.post('/api/project/build', ctx => {
// 接管我的项目绝对路径
const {projectPath} = ctx.request.body;
try {
// 先响应
ctx.body = {msg: '开始构建,请注意下方的构建信息。'}
// 再执行构建
buildProject(projectPath)
} catch (error) {
ctx.body = {msg: error}
}
})
child_process 执行打包命令
简略的封装了一个执行脚本的函数,前面再通过 callback 进行音讯的推送
/**
* 执行命令
* @param {String} script 须要执行的脚本
* @param {Function} callback 回调函数
* @returns
*/
const implementCommand = async (script, callback) => {callback(script)
return new Promise((resolve, reject) => {
try {const sh = child_process.exec(script, (error, stdout, stderr) => {
// 这里的 stdout stderr 在执行之后才会触发
if (error) {reject(error);
callback(error)
}
resolve()});
// 胜利的推送
sh.stdout.on('data', (data) => {callback(data)
})
// 谬误的推送
sh.stderr.on('data', (error) => {callback(error)
})
} catch (error) {callback(error)
reject()}
})
}
打包我的项目 + 音讯推送
buildProject
只负责整合须要执行哪些命令
整合之后调用 implementCommand
执行命令
implementCommand
会执行 messagePush
进行音讯推送。
/**
* 打包我的项目
* @param {String} projectPath 打包门路
*/
const buildProject = async projectPath => {
// 执行 install 命令
await implementCommand(`cd ${projectPath} && yarn install`, messagePush)
// 执行 build 命令
await implementCommand(`cd ${projectPath} && yarn build`, messagePush)
messagePush('打包实现!!!')
}
/**
* 音讯推送
* @param {String} content 须要推送的内容
*/
const messagePush = content => {clientList.forEach(sse => sse.send(`[${currentTime()}] ${content}`))
// send 自定义事件写法
// clientList.forEach(sse => sse.send({ data: content, event: 'push'}))
}
服务端执行过程示意图
到这里服务端就曾经全副实现了,看得出来构建 + 音讯推送是最麻烦的一步,所以我画了个草图。
什么?看不懂?放过我吧,就这程度 …
前端开发
本次需要,前端比较简单,只须要三步
- 连贯 SSE (调用 通信 API)
- 构建我的项目 (调用构建 API)
- 接管到 SSE 的推送进行内容展现
HTML 构造
<!-- 输出我的项目的门路 -->
<input type="text" value="C:/xxx" id="dirPath">
<!-- 构建点击的按钮 -->
<button id="buildSubmit"> 构建 </button>
<!-- 输入内容的载体 -->
<pre><code id="app"></code></pre>
开启 SSE 连贯
// 通过 new EventSource 开启 SSE
const source = new EventSource(`http://127.0.0.1:3000/api/log/push`);
// 监听 message 事件
source.onmessage = event => {
// 挂到载体下面
app.innerHTML += `${event.data} \n`
}
调用构建 API
通过构建按钮点击之后触发调用构建接口
buildSubmit.onclick = () => {
// 构建之前清空载体
app.innerHTML = '';
const projectPath = dirPath.value;
// 做了个简略校验
if(!projectPath) return alert('指标打包门路不能为空');
// 发动申请
$.ajax({
url: 'http://127.0.0.1:3000/api/project/build',
method: 'post',
data: {projectPath // 我的项目门路},
// 胜利回调
success: res => {alert(res.msg)
}
})
}
到这里咱们就曾经开发完了这个需要,
大家能够入手去试一试当然这只是 SSE 一些根本的应用而已。
写在前面
如果大家对这篇文章有任何质疑,能够留言或者私信给我。我将逐个回复(我理解的就回复的快一些,不晓得的,我会查问材料再进行回复,就能够略微慢一些~)
注解
[0]
Event — https://developer.mozilla.org…
[1]
RFC6455 — https://www.rfc-editor.org/rf…
[2]
event-source-polyfill — https://www.npmjs.com/package…
[3]
child_process — https://nodejs.org/dist/lates…
[4]
Server-Sent Events 教程 — http://www.ruanyifeng.com/blo…