当前位置: 首页 > 后端技术 > Node.js

通过Node + SSE 做了一个构建日志推送

时间:2023-04-03 14:38:57 Node.js

构建日志是否通过Node+SSE推送什么是SSE?SSE的全称是ServerSentEvent,翻译过来就是服务器派发事件。网页通常需要向服务器发送请求以获取新的数据,即向服务器请求的页面。使用服务器发送事件,服务器可以随时向我们的网页推送数据和信息。推送的信息可以在本页面处理为Events[0]+data。白话SSE的本质其实就是一个HTTP长连接,只不过它发送给客户端的不是一次性的数据包,而是text/event-stream格式的流。所以客户端不会关闭连接,会一直等待服务器发来的新数据流。SSE和其他交互方式的区别SSE和WS有什么区别?模式协议交互通道内容编码重连事件类型总结SSEHTTP服务器单向推送默认文本支持断线重连默认支持自定义消息类型轻量级WebSocketWS(基于TCP传输层的应用层协议,RFC6455[1]为其定义标准)二-方式推送默认二进制手动实现无扩展性,功能强大TCP/IP五层模型SSE兼容性APISSE6.079.06.05.011.5WS4.012114.212.1IE不支持?兼容性不好?event-source-polyfill助你圆SSE梦下面的例子不会用这个event-source-polyfill来实现。考虑兼容性的认真的人?狗头防御身.jpgSSEAPI属性(只读)name函数类型备注readyState当前状态Number0--connecting1--open2--closedurl当前连接地址StringwithCredentials是否开启凭证收集Boolean方法名函数返回值closeclient主动关闭连接-事件名称函数返回值onclose连接关闭触发eventonopen连接打开触发eventonmessage服务器消息推送消息触发事件服务器API字段名actiontyperemark数据传输文本String(默认)\可传输JSON可多行累加event事件nameString可以自定义定义当前推送的ididString作为消息的标识retrytimeoutretrytime客户端感知到服务端连接异常后的次数。重连需求审核将通过重试设置时间进行。我们已经对SSE和它的一些API有了初步的了解。接下来我们需要做的是了解需求和需求。需求评审结束后,产品经理会给你一张好图,你自己看吧。技审前面的**产品经理居然只给了一张图。当用户打开窗口时,将建立SSE连接。服务器需要建立这个连接并保存在连接池中。如果用户点击Build,打包时后台程序会执行打包命令推送消息,然后前端显示消息。接口回顾GETapi/log/push(通讯API)连接SSE通道,进行后续消息通讯POSTapi/project/build(构建API)参数projectPath传入你需要构建的项目路径进行项目构建,触发在构建消息推送和等待的Calmdown时~构建API是不是太简单了?怎么给我们打包?nodejs中有一个child_process[3]模块叫做childprocess,通过它可以执行install和build。如何触发通信API消息推送?child_process可以异步推送消息,里面会有stdout和stderrhook。感知来自命令行的正常和异常输出。如果我们在这一步实时推送消息,岂不是很棒。回答完这两个问题,我们就可以愉快的写代码了。后端开发引入需要用到的包。这些包似乎被引用了很多。其实只有koa-sse-stream和SSE有关,其实就是别人封装的一个中间件,如果你看过《Server-Sent Events 教程》[4]constKoa=require('koa');constrouter=require('koa-router')();constKoaSSEStream=require('koa-sse-stream');//打包的SSE中间件constchild_process=require('child_process');//节点子进程constbodyParser=require('koa-bodyparser');constcors=require('@koa/cors');constmoment=require('moment');constnewDate=()=>moment().format('YYYY-MM-DDHH:mm:ss');constapp=newKoa();app.use(cors());app.use(bodyParser());/*下一段代码存放的地方*/app.use(router.routes())app.listen(3000);通信API我们已经说过在此之前每增加一个连接,都会放到连接池中,而这个连接池就相当于一个通讯录。后面会遍历这个通讯录来推送消息//连接池constclientList=[];//koa-sse-stream配置constSSE_CONF={maxClients:2,//最大连接数pingInterval:40000//重连时间}router.get('/api/log/push',KoaSSEStream(SSE_CONF),ctx=>{//每个连接都会做一次推送clientList.push(ctx.sse);})buildAPI我们在这个接口调用,先响应,否则接口会等到构建完成才响应。没有人知道构建某个项目需要多长时间,并且可能在设置的请求超时时间过后没有响应。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执行打包命令简单封装一个函数执行脚本,然后发送消息通过回调Push/***Executecommand*@param{String}script待执行脚本*@param{Function}callback回调函数*@returns*/constimplementCommand=async(script,callback)=>{callback(script)returnnewPromise((resolve,reject)=>{try{constsh=child_process.exec(script,(error,stdout,stderr)=>{//这里的stdoutstderr执行完会被触发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包路径*/constbuildProject=asyncprojectPath=>{//执行安装命令awaitimplementCommand(`cd${projectPath}&&yarninstall`,messagePush)//执行构建命令awaitimplementCommand(`cd${projectPath}&&yarnbuild`,messagePush)messagePush('打包完成!!!')}/***消息推送*@param{String}content要推送的内容*/constmessagePush=content=>{clientList.forEach(sse=>sse.send(`[${currentTime()}]${content}`))//发送自定义事件写法//clientList.forEach(sse=>sse.send({data:content,event:'push'}))}服务端执行流程示意图到这里服务端就已经完成了。可以看出构建+消息推送是最麻烦的一步,所以画了个草图。什么?无法阅读?放开我,就这层...前端开发需要这个时间,前端比较简单,只需要三步连接SSE(调用通信API)构建项目(调用构建API)接收SSE推送显示内容HTML结构构建

打开SSE连接//通过newEventSourceconst打开SSEsource=newEventSource(`http://127.0.0.1:3000/api/log/push`);//监听消息事件source.onmessage=event=>{//挂在载体上app.innerHTML+=`${event.data}\n`}点击构建按钮后调用构建API触发构建接口的调用buildSubmit.onclick=()=>{//构建前清除载体app.innerHTML='';constprojectPath=dirPath.value;//做了一个简单的验证if(!projectPath)returnalert('目标打包路径不能为空');//发起请求$.ajax({url:'http://127.0.0.1:3000/api/project/build',method:'post',data:{projectPath//项目路径},//成功回调成功:res=>{警报(res.msg)}})}至此我们就开发出了这个需求,大家可以自行尝试。当然,这只是SSE的一些基本使用。如果对本文有任何疑问,可以给我留言或私信,我会一一回复(知道的会回复的更快,不知道的会查看信息再回复,可以慢一点~)注解[0]事件--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-SentEvents教程--http://www.ruanyifeng.com/blo...