服务粉丝

我们一直在努力
当前位置:首页 > 财经 >

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

日期: 来源:ELab团队收集编辑:ELab.liruiliang

大厂技术  坚持周更  精选好文

本文为来自 字节教育-智能学习-前端团队 的文章,已授权 ELab 发布。

智能学习前端团队 自创立以来,团队专注于打破大众对教育的刻板印象,突破固有的教学思维,攻破各类教学屏障。旨在为每一位学生制定最合适的学习方案,予以因材施教,使优质教育随‘触’可达。

背景

在特定场景下,我们往往需要实时的去获取最新的数据,如获取消息推送或公告、股票大盘、聊天消息、实时的日志和学情等,都对数据的实时性要求很高,面对这类场景,最常用的可能就是轮询,但除了轮询还有长连接(Websocket)和服务端推送(SSE)方案可供选择。

轮询

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

轮询 (polling)

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

image.png

轮询 的优缺点

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

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

轮询 (long polling)

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

image.png

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

轮询 的优缺点

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

  • 服务端资源大量消耗: 服务端会一直hold住客户端的请求,这部分请求会占用服务器的资源。对于某些语言来说,每一个HTTP连接都是一个独立的线程,过多的HTTP连接会消耗掉服务端的内存资源。
  • 难以处理数据更新频繁的情况: 如果数据更新频繁,会有大量的连接创建和重建过程,这部分消耗是很大的。虽然HTTP有TCP连接复用,但每次拿到数据后客户端都需要重新请求,因此相对于WebSocket和SSE它多了一个发送新请求的阶段,对实时性和性能还是有影响的。

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

轮询 ,长轮询怎么选?

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

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

长连接

WebSocket

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

image.png

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

请求特征

请求头特征

  • HTTP 必须是 1.1 GET 请求
  • HTTP Header 中 Connection 字段的值必须为 Upgrade
  • HTTP Header 中 Upgrade 字段必须为 websocket
  • Sec-WebSocket-Key 字段的值是采用 base64 编码的随机 16 字节字符串
  • Sec-WebSocket-Protocol 字段的值记录使用的子协议,比如 binary base64
  • Origin 表示请求来源

响应头特征

  • 状态码是 101 表示 Switching Protocols
  • Upgrade / Connection / Sec-WebSocket-Protocol 和请求头一致
  • Sec-WebSocket-Accept 是通过请求头的 Sec-WebSocket-Key 生成

兼容性

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”)来分隔。每个事件的数据可能由多行组成。

image.png

和 Websocket对比

SSEWebSocket
单向:仅服务端能发送消息双向:客户端、服务端双向发送
仅文本数据二进制、文本都可
常规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,监听openmessageerror

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团队 收货大厂一手好文章~

智能学习前端团队 自创立以来,团队专注于打破大众对教育的刻板印象,突破固有的教学思维,攻破各类教学屏障。旨在为每一位学生制定最合适的学习方案,予以因材施教,使优质教育随‘触’可达。

  • 字节跳动校/社招内推码: ERSK6WM
  • 投递链接: https://job.toutiao.com/s/k4H1XUJ

可凭内推码投递 字节教育 - 智能学习前端团队 相关岗位哦~

参考资料

[1]

rfc6455.pdf: https://datatracker.ietf.org/doc/pdf/rfc6455.pdf

[2]

WebSocket协议中文版(rfc6455): https://blog.csdn.net/aigoogle/article/details/122281445

[3]

深入剖析WebSocket的原理 - 知乎: https://zhuanlan.zhihu.com/p/32845970

[4]

HTTP长连接实现原理 - 掘金: https://juejin.cn/post/6923887573861564423

[5]

WebSocket() - Web API 接口参考 | MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/WebSocket/WebSocket

[6]

EventSource - Web API 接口参考 | MDN: https://developer.mozilla.org/zh-CN/docs/Web/API/EventSource

- END -

相关阅读

  • 通知 | 中华医学会期刊全文数据库临时停止服务

  • 中华医学会期刊全文数据库临时停止服务通知因用户系统和业务系统需求,中华医学会杂志社拟对旗下各网站(平台)进行全面升级。升级过程中,各系统(平台)临时停止服务。涉及本校图书馆
  • 智慧农业 | 大数据应用于农业的十大案例

  • 基于物联网等技术的应用,农业领域积累了大量的数据,为大数据应用于农业奠定了基础。从国内国际的发展来看,大数据正在驱动农业发展路径发生变化,以提高农业效率,保障食品安全,实现
  • 中国数字农业还处于初级阶段,未来破局靠这4点

  • “ 数字化是科技赋能农业最有力的体现。农业是国民经济发展的关键基础,同时也是国家安全的重要保障,自古以来,我国对于农业发展就极为重视。但近年来,农业资源和需求间的矛盾不
  • 农业大数据下的智慧农业发展

  • 01农业大数据对智慧农业的重要性 随着云时代的来临,大数据也吸引了越来越多的关注。物联网的存在使这种基于大数据的采集以及分析变成了一种可能,2009年以来,在国家政策积极鼓
  • 7个极其重要的Pandas函数!

  • 公众号:尤而小屋作者:Peter编辑:Peter本文介绍Pandas中7个极其重要的函数,它们在日常的数据分析会经常使用。isnullapplygroupbyuniquereset_indexsort_valuesto_csv同时你还将
  • Python搞定表格可视化!

  • 分享一个Python工具plottable,轻松制作高度个性化的表格,底层为Matplotlib。例如这样的,或者这样的,第一张图详细代码:# 导入相关包from pathlib import Pathimport matplotlibim
  • 连中两篇WWW顶会!

  • Datawhale干货 作者:鸣也,中国科学院大学,Datawhale成员转载自公众号:Datawhale很幸运能在WWW23中稿两篇论文,作为22年中旬才开始做深度学习方面的科研新手来说,给即将入门科研的
  • Python的那些"地图"神器!

  • 老表荐书图书介绍:《Python金融量化分析学习教程》本书是有关Python在金融量化分析领域应用的一本从入门到精通类图书。全书分4篇共10章。第1篇(第1~3章)简单介绍了Python的基

热门文章

  • “复活”半年后 京东拍拍二手杀入公益事业

  • 京东拍拍二手“复活”半年后,杀入公益事业,试图让企业捐的赠品、家庭闲置品变成实实在在的“爱心”。 把“闲置品”变爱心 6月12日,“益心一益·守护梦想每一步”2018年四

最新文章

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

  • 大厂技术 坚持周更 精选好文本文为来自 字节教育-智能学习-前端团队 的文章,已授权 ELab 发布。智能学习前端团队 自创立以来,团队专注于打破大众对教育的刻板印象,突破固有
  • 直播内容团队核心成员和具体分工

  • 必看:如果你想在直播带货这个赛道,1~2年内越走路越宽,那么内容团队就是你的特种部队;年份销售额推广费20211.1个亿1200万20221.2个亿600万你看,随着竞争越来越激烈,我们的销售额没
  • 直播带货 陪跑业务升级版 超80%成功率,凭什么?

  • 关于六哥团队:专注电商和直播带货13年,只服务做长期直播带货,赚快钱,玩黑科技的,擦边球,踩红线的,请绕道!虽然有一场直播3个小时,170万的经验,服务近400家不同类目的学员,自播账号一年
  • 上海新展预告 | 爱,此处此刻

  • 「爱,此处此刻」富士X-SPACE x 原画册2023.2.14 — 2023.3.8X-SPACE富士影像共享空间爱爱,无论时态,无论人事物,那些给予我们力量的任意时刻,按下的每一次快门,凝固的每一个瞬间,在
  • 一瞬一世|小展览.78

  • 原画册投稿/社群成员作品选登第78期本期选片人:赵童话的爸爸烟火·雪地中的追逐© 安放点评:很喜欢安放的这张照片,让我能感受到父亲对孩子独有的温柔。画面中失焦的烟花上呈现