当前位置: 首页 > Web前端 > JavaScript

Web项目轮询实践

时间:2023-03-27 17:40:05 JavaScript

最近使用了两种不同的方式来实现即时通信:一种是基于timer的轮询(与项目内后端协作),一种是基于websocket的双向通信(项目与第三方集成和协作)。接下来,我们将介绍这两种不同的即时通讯方式的基本用法和优缺点。第一种:Ajax轮询轮询需求场景:显示的面板数据需要实时刷新。实现过程:客户端主动向服务端发送请求,等待一段固定的时间(通常使用JavaScript的setInterval函数),然后再次发送请求setInterval(function(){fetch('get-csrf').then((res)=>res.json()).then((res)=>{console.log(res);});},5*1000);优点:实现简单,不需要特定的服务端功能,客户端即可处理。并且所有浏览器都支持,良好的错误处理系统,超时管理缺点:浪费服务器和网络资源:大部分链接无效,重复响应数据有延迟:setInterval的时间间隔设置的越长,新数据上server到达客户端的时间越长,非可伸缩响应的结果没有顺序:因为是异步请求,当发送的请求没有返回结果时,会再次发送后续的请求,而在这个时候如果后面的请求返回结果早于前面的请求,那么前面的请求返回结果数据的时候,就已经过时无效了。场景:每隔5s定时请求接口vs接口返回,然后每隔5s获取请求响应,然后每隔5s执行一次。可以保证响应的顺序,但同时需要考虑到当请求没有响应时,后面的操作将不会执行。可以增加超时响应的处理,如果超过一定时间没有响应则取消请求。超时时间还要考虑到其他需要时间过长的界面,没有实时刷新功能防止取消超时后没有数据显示。实现fetch请求超时取消请求的方式可以通过timeout+abort方法实现。Promise.race([fetch(),timout])方法也可以实现超时功能//接口返回后,每隔5s获取该功能。getToken(){fetch('get-csrf').then((res)=>res.json()).then((res)=>{console.log(res);setTimeout(()=>{getToken();},5*1000);});}getToken();longPolling实现过程:客户端向服务端发送请求接口,然后等待服务端响应。服务器端需要实现一个特定的功能来允许请求被挂起。只要有事件发生,服务器端就会在挂起的请求中发送响应并关闭请求。客户端将使用此响应并打开一个新响应。服务器端长期存在的Ajax请求。与上面提到的客户端主动轮询的方式相比,服务端需要有一个特殊的功能来临时挂起连接。在项目实现过程中,对于前端人员,采用第一种方式,实现速度更快,可操作性更高客户端代码示例:functiongetToken(){  fetch('get-csrf').then((res)=>res.json()).then((res)=>{console.log(res);});}getToken();/**这种长轮询方式下,客户端在XMLHttpRequest就绪状态时4(即数据传输结束),调用回调函数处理信息。当readystate为4时,数据传输结束,连接已经关闭**/其他轮询方式script标签&IframeStream的长轮询:实现方式:在页面上附加script标签,让脚本执行。服务器端会挂起连接,直到有事件发生,然后将脚本内容发回给浏览器,然后重新打开另一个脚本标签来获取下一个事件。(script标签的src或者iframe的src指向服务器地址)没有办法实现可靠的错误处理或者跟踪连接状态,并且具有跨域功能,实现方式更多比如cors第二种:websocketfirst这种轮询的方式是基于HTTP方式实现的。HTTP连接是被动的(需要一端主动发起)、无状态的、单向的、非持久的。同时轮询实现的数据延迟(时间间隔)和重复请求的浪费是缺点。对于项目场景如:频繁请求更新数据:当控制台操作2D地图时,需要立即将地图视野的变化(平移、缩放、旋转等)传输到第三方的3D地图-方综合应用,同步响应。操作实时性要求高,响应频繁。多用户通信,由于集成网页的方式,通过chromium加载不同的页面,这些页面相当于独立的浏览器页面,各个页面之间通信,页面与第三方集成应用之间也需要通信,所以综合考虑websocket的双向实时通信能力更能满足业务选择。websocket与WebSocket的连接首先进行TCP的三次握手,然后依赖HTTP协议进行一次握手。握手成功后,直接从TCP通道传输数据,与HTTP无关。客户端:当客户端连接到服务器时,会向服务器发送类似下面的http报文,升级协议GETws://localhost:8080/socket.io/?UserGroup=toyGroup&EIO=3&transport=websocketHTTP/1.1Host:http://localhost:8080Connection:UpgradeUpgrade:websocketOrigin:http://localhost:8000Sec-WebSocket-Version:13Sec-WebSocket-Key:V1yj21hlXCrSK2HDuJsD9A==Sec-WebSocket-Extensions:permessage-deflate;client_max_window_bitsConnection表示:Upgr升级协议Upgrade:websocket:其作用是告诉服务端需要将通信协议切换为websocketSec-WebSocket-Version:13:表示websocket的版本。如果服务器不支持该版本,则需要返回一个Sec-WebSocket-Version头,其中包含服务器支持的版本号。Sec-WebSocket-Key:与服务器后面响应头中的Sec-WebSocket-Accept匹配,提供基本的保护,如恶意连接或无意连接。服务器:如果服务器支持websocket协议,则将通信协议切换为websocket,并向客户端发送类似如下的响应头HTTP/1.1101SwitchingProtocolsDate:Mon,20Dec202107:14:21GMTConnection:upgradeUpgrade:websocketSec-WebSocket-Accept:8L5tW9vVu5z9vfRMglkhare9o58=返回状态码为101,表示客户端同意协议转换请求,并转换为websocket协议。Sec-WebSocket-Accept是根据客户端请求头的Sec-WebSocket-Key计算出来的。websocket是使用服务端实现的:在nodejs中,使用ws模块实现constWebSocket=require('ws');constwss=newWebSocket.Server({port:8080});wss.on('connection',functionconnection(ws){console.log('Serverconnection');ws.on('message',function(message){//msessage默认为Bufferconsole.log('Serverreceives:',message.toString());});ws.send('world',{binary:false});});客户端初始化websocket实例importReact,{useState,useEffect}from'react';importstylesfrom'./index.less';interfaceSocketDemoProps{}constSocketDemo:React.FC=()=>{const[msg,setMsg]=useState([]);useEffect(()=>{varws=newWebSocket('ws://localhost:8080');ws.onopen=function(){msg.push('客户端连接:成功');setMsg([...msg]);ws.send('你好');};ws.onmessage=function(e){msg.push('客户端收到消息:'+e.data);setMsg([...msg]);};},[]);返回({msg.map((item)=>({item}))}

);};exportdefaultSocketDemo;Client输出客户端连接:成功客户端收到消息:worldserver输出服务器连接服务器接收://默认是Buffer,可以使用toString转换成对应的字符串。服务器连接服务器,收到:hellowebsocket'sOpcodeopcode中定义了帧的类型:连续帧:0:继续上一帧;表示非控制帧,与前一帧类型完全相同:主要用于传输数据1:文本帧(UTF8)2:二进制帧3-7:为非控制帧保留控制帧8:关闭帧:当ws链接关闭时,会有一个关闭的frame9:HeartbeatframepingA:HeartbeatframepongB-F:Tocontrolreservedframewebsocket服务构建:socket.ionode.js提供了一个高效的服务端运行环境,但是由于浏览器对HTML5的支持不同。为了兼容所有浏览器,提供优秀的实时用户体验,并为程序员提供客户端和服务端一致的编程体验,socket.io诞生了Socket.IO封装了Websocket,基于Node的JavaScript框架,包括客户端JavaScript和服务器节点。它屏蔽了所有底层细节,让顶层调用变得非常简单。此外,Socket.IO还有一个很重要的好处。它不仅支持WebSocket,还支持多种轮询机制和其他实时通信方式,并封装了通用接口。这些方法包括Adob??eFlashSocket、Ajax长轮询、Ajax多部分流、持久Iframe、JSONP轮询等。也就是说,当Socket.IO检测到当前环境不支持WebSocket时,可以自动选择最佳方式实现网络的实时通信constsocket=io("127.0.0.1:8080",{transports:["websocket","polling"]//注意:transports属性可以直接为websocket,不设置时默认使用polling});socket.on("connect_error",()=>{//还原为经典升级socket.io.opts.transports=["polling","websocket"];});提供的特性可靠性:即使存在应用环境仍然可以建立连接:代理或负载均衡器个人防火墙或杀毒软件支持自动连接:除非另有说明,否则断开连接的客户端将不断重新连接到服务器,直到服务器可用再次。重新连接设置import{io}from"socket.io-client";constsocket=io({reconnection:false//自动重连设置为false后,需要手动设置重连});consttryReconnect=()=>{setTimeout(()=>{socket.io.open((err)=>{if(err){tryReconnect();}});},2000);}socket.io.on("关闭",tryReconnect);断开检测:在http://Engine.io层实现了心跳机制,可以让客户端和服务器知道其中一方没有响应。该功能是通过在服务端和客户端设置定时器来实现的。连接握手时,服务端会主动通知客户端心跳间隔和超时时间。浏览器连接后断开连接重新计时的默认设置。chrome的Network的ws面板可以查看心跳检测设置pingInterval:25000pingTimeout:5000Binarysupport:任何序列化的数据结构都可以发送Cross-browsersupport:该库甚至支持IE8Supportmultiplexing:forapplication创建的重点隔离在该程序,http://Socket.io允许你创建多个命名空间,这些命名空间有独立的通信通道,但会共享相同的底层连接来支持Room:在每个命名空间下,你可以定义任意数量的通道,我们呼叫“房间”,您可以加入或离开房间,甚至可以向指定房间广播消息。开发版问题跨域socket.iov3和socket.iov2的一个变化是:socket.iov2默认支持跨域,socket.iov3需要手动支持socket.iov3版本客户端连接socket.iov2版本服务器?Socket.iov3服务器连接socket.iov2客户端constio=require("socket.io")({allowEIO3:true//默认为false});更多信息:https://socket.io/blog/socket...websocket开发过程中遇到的问题socket服务器需要提供capabilities命名空间和组分组状态同步服务器并发连接数

猜你喜欢