文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

数据不够实时:试试长连接?

2024-12-13 15:31

关注

轮询

轮询就是采用循环http请求的方式,通过重复的接口请求去获取最新的数据。

短轮询 (polling)

短轮询可能是我们用的最多的一种实时刷新数据的方式了,我们在讲轮询方案时,大部分指的就是短轮询,其实现方式和普通的接口无异,改造也只要前端增加定时器或useRequest配置轮询参数即可,其原理也非常简单,如下图,如果是http1.1及以上,TCP连接可以复用,当然http1.0及以下也是可以使用,但消耗会更多。短轮询的特点就是接口请求立即会返回,每次请求都可以理解为是一次新的请求。

短轮询的优缺点

短轮询最大的优点就是简单,前端设置时间间隔,定时去请求数据,而服务端只需同步的查询数据返回即可,但缺点也显而易见:

  1. 无用请求过多:从下图可以看出,每隔固定时间,一定有请求发出,且每次接口可能返回一样的数据或返回空结果,服务端会重复查询数据库、前端会重复重渲染
  2. 实时性不可控,如数据更新了,但轮询请求刚结束一轮,会造成轮询间隔内数据都得不到更新

长轮询 (long polling)

看完了上面关于短轮询的介绍,我们知道了轮询有两个主要的缺陷:一个是无用请求过多,另外一个是数据实时性不可控。为了解决这两个问题,于是有了更进一步的长轮询方案。

在上图中,客户端发起请求后,服务端发现当前没有新的数据,这个时候服务端没有立即返回请求,而是将请求挂起,在等待一段时间后(一般为30s或者是60s,设置一个超时返回主要是为了考虑过长的无数据连接占用会被网关或者某层中间件断开甚至是被运营商断开),如发现还是没有数据更新的话,就返回一个空结果给客户端。客户端在收到服务端的回复后,立即再次向服务端发送新的请求。这次服务端在接收到客户端的请求后,同样等待了一段时间,这次好运的是服务端的数据发生了更新,服务端给客户端返回了最新的数据。客户端在拿到结果后再次发送下一个请求,如此反复。

长轮询的优缺点

长轮询很完美地解决了短轮询的问题,首先服务端在没有数据更新的情况下没有给客户端返回数据,所以避免了客户端大量的重复请求。再者客户端在收到服务端的返回后,马上发送下一个请求,这就保证了更好的数据实时性。不过长轮询也有缺点:

从上面的描述来看,长轮询的次数和时延似乎可以更少,那是不是长轮询更好呢?其实不是的,这个两种轮询方式都有优劣势和适合的场景。

短轮询 ,长轮询怎么选?

长 轮询多用于操作频繁,点对点的通讯,而且连接数不能太多情况,每个TCP连接都需要三步握手,这需要时间,如果每个操作都是先连接,再操作的话那么处理速度会降低很多,所以每个操作完后都不断开,次处理时直接发送数据包就OK了,不用建立TCP连接。例如:数据库的连接用长连接, 如果用短连接频繁的通信会造成socket错误,而且频繁的socket 创建也是对资源的浪费。

而像WEB网站的http服务一般都用短 轮询,因为长连接对于服务端来说会耗费一定的资源,而像WEB网站这么频繁的成千上万甚至上亿客户端的连接用短连接会更省一些资源,如果用长连接,而且同时有成千上万的用户,如果每个用户都占用一个连接的话,那可想而知吧。所以并发量大,但每个用户无需频繁操作情况下需用短连好。

长连接

​WebSocket​

上面说到长轮询不适用于服务端资源频繁更新的场景,而解决这类问题的一个方案就是WebSocket。用最简单的话来介绍WebSocket就是:客户端和服务器之间建立一个持久的长连接,这个连接是双工的,客户端和服务端都可以实时地给对方发送消息。下面是WebSocket的图示:

WebSocket对于前端的同学来说是非常常见了,因为无论是webpack还是vite,用来HMR的reload就是通过WebSocket来进行的,有代码改动,工程重新编译,新变更的模块通知到浏览器加载新的模块,这里的通知浏览器加载新模块就是通过WebSocket的进行的。如上图,通过握手(协议转换)建立连接后,双方就保持持久连接,由于历史的关系,WebSocket建立连接是依赖HTTP的,但是其建连请求有明显的特征,目的是客户端和服务端都能识别并保持连接。

请求特征

请求头特征

响应头特征

兼容性

WebSocket 协议在2008年诞生,2011年成为国际标准。现在所有浏览器都已经支持了。

实现一个简单的 WebSocket

基于原生WebSocket我们实现一个简单的长连。

连接

// 连接只需实例一个WebSocket
const ws = new WebSocket(`wss://${url}`);

发送消息

  ws.send("这是一条消息:" + count);

监听消息

ws.onmessage = function (event) {
console.log(event.data);
}

关闭连接

ws.close();

​在工程上使用WebSocket​

在工程上,很少直接基于原生WebSocket实现业务需求,使用WebSocket需要完成下面几个问题:

服务端推送(SSE)

SSE全称Server-sent Events,是HTML 5 规范的一个组成部分,该规范十分简单,主要由两个部分组成:第一个部分是服务器端与浏览器端之间的通讯协议,第二部分则是在浏览器端可供 JavaScript 使用的 EventSource 对象。通讯协议是基于纯文本的简单协议。服务器端的响应的内容类型是“text/event-stream”。响应文本的内容可以看成是一个事件流,由不同的事件所组成。每个事件由类型和数据两部分组成,同时每个事件可以有一个可选的标识符。不同事件的内容之间通过仅包含回车符和换行符的空行(“rn”)来分隔。每个事件的数据可能由多行组成。

​和 Websocket对比​

SSE

WebSocket

单向:仅服务端能发送消息

双向:客户端、服务端双向发送

仅文本数据

二进制、文本都可

常规HTTP协议

WebSocket协议

​兼容性​

​数据格式​

服务器向浏览器发送的 SSE 数据,必须是 UTF-8 编码的文本,

响应头

Content-Type: text/event-stream
Cache-Control: no-cache
Connection: keep-alive

数据传输

服务端每次发送消息,由若干message​组成,使用\n\n​分隔,如果单个messag​过长,可以用\n分隔。

field取值

data
event
id
retry

例子

// 注释,用于心跳包
: this is a test stream\n\n
// 设置断链1000ms重试一次
retry:1000 \n\n
event: 自定义消息\n\n

data: some text\n\n

data: another message\n
data: with two lines \n\n

​实现一个简单的SSE​

web端

实例化EventSource​,监听open、message、error

const source = new EventSource(url, { withCredentials: true });
// 监听消息
source.onmessage = function (event) {
// handle message
};
source.addEventListener('message', function (event) {
// handle message
}, false);

// 监听错误
source.onerror = function (event) {
// handle error
};
source.addEventListener('error', function (event) {
// handle error
}, false);

// 关闭连接
source.close()

服务端

以nodejs为例,服务端代码和普通请求无异,并没有新的处理类库。

    res.writeHead(200, {
"Content-Type":"text/event-stream",
"Cache-Control":"no-cache",
"Connection":"keep-alive",
"Access-Control-Allow-Origin": '*',
});
res.write("retry: 10000\n\n");
res.write("event: connecttime\n\n");
res.write("data: " + (new Date()) + "\n");
res.write("data: " + (new Date()) + "\n\n");

// 模拟收到消息推送给客户端
interval = setInterval(function () {
res.write("data: " + (new Date()) + "\n\n");
}, 1000);

和WebSocket不同,SSE并不是新的通信协议,其本质是在普通HTTP请求的基础上定义一个Content-Type​,保持上连接,通过普通的接口也能模拟出SSE的效果,以XMLHttpRequest为例

     const xhr = new XMLHttpRequest();
xhr.open("GET", "http://localhost:8844/long", true);
xhr.onload = (e) => {
console.log("onload", xhr.responseText);
};
xhr.onprogress = (e) => {
// 每次服务端写入response的数据,都会传输过来,并产生一次onprogress事件
console.log("onprogress", xhr.responseText);
};
xhr.send();

参考文献

rfc6455.pdf[1]

WebSocket协议中文版(rfc6455)[2]

深入剖析WebSocket的原理 - 知乎[3]

HTTP长连接实现原理 - 掘金[4]

WebSocket() - Web API 接口参考 | MDN[5]

EventSource - Web API 接口参考 | MDN[6]​

来源:ELab团队内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯