水羊水羊,又水又痒,做一整只快乐的小肥羊~
作者:水羊
来源:https://mp.weixin.qq.com/s/-KqKe5-HgqSrQbrj9op7pA
一道面试题
一切的起源,还得先回顾一道经典的面试题,从 URL 输入到页面展现到底发生什么?
相信这是一道BAT常用的面试题,经常逛掘金的同学一定看过这个图
按图中的顺序分解下来就是
- DNS 解析:将域名解析成 IP 地址
- TCP 连接:TCP 三次握手
- 发送 HTTP 请求
- 服务器处理请求并返回 HTTP 报文
- 浏览器解析渲染页面
- 断开连接:TCP 四次挥手
另外从图中,我们也可以发现,数据通信作为信息传递的载体,在整个交互流程中,发挥了至关重要的基建作用。
过去的十年
让我们把时光倒回到10年前,那是2011年的夏天,Facebook横空出世BigPipe技术,风靡了整个前端圈。
人们第一次正式在大型商业web前端项目中,基于浏览器协议,开始考虑可能的优化手段。
基于当时主流的http1.1协议,我们把目光投向了Chunked transfer encoding
基于分块输出的能力,我们可以把传统的数据交互切分成类似放电影一样的帧请求
标准的http1.1请求模型如下图
基于Chunked transfer encoding拆包之后变成
对比两张图,我们可以欣喜的发现,我们可以有效利用原来一次URL页面请求的传输TTFB(Time To First Byte)时间,拆分成多次C/S之间的交互模型,来有效加快first Byte到端的时间,来实现提前渲染
而原本的模型中,在网络传输的时刻,端的计算能力是并行,且空闲的
(当然在BigPipe技术出现之前,也有是很多其他老前辈的探索,比如基于activeX或者flash插件做多帧交互等等,有兴趣的同学可以在后续的前端读历史系列文章中一窥究竟。)
随着时光飞逝,转瞬到了2019年,这个时候,Web主流的浏览器协议已经从http1.1升级到了http2.0,对比http1.1主要有了一下几个方面的改动
http1.1 | http2.0 | |
---|---|---|
分块传输 | Chunked encoding,文本传输 | 帧,二进制压缩 |
长链接 | Keep alive,减少tcp握手耗时,但是还是有线头阻塞(Head of line blocking) 的问题,即一个 TCP 连接一次只能发出一个请求,所以客户端必须等待收到响应后才能发出另外一个请求,这样耗时的请求如果在前面会 block 后面耗时的请求。 | 在基础上增加了管道复用,即同域名下所有通信都在单个连接上完成,同个域名只需要占用一个 TCP 连接,使用一个连接并行发送多个请求和响应。同时,单个连接可以承载任意数量的双向数据流,单个连接上可以并行交错的请求和响应,之间互不干扰。 |
http请求头 | 明文,标准,不压缩 | 压缩 |
浏览器对连接数的限制 | 有,大家常听到的8个,具体应该和app和端有关系 | 因为一个tcp连接可以用来发多个请求,所以限制只存在于webServer的tps机能 |
Server Push | 无 | 有,在接收到请求后,服务端可以发送其他资源推送和端确认,缺点是端无法主动pull,而server端需要配置或者编程支持(依赖的来源也只能是端发起的请求头信息),相比socket模型可编程属性会弱很多 |
减少原来1.1时代的优化 | 1. Css Sprint(导致没法Cache) |
- 拆分assert到多域名(希望并发)
- Js CDN Combine | 1. 直接资源和URI对应Cache
- 单域名链接即可并发,不再需要拆分
- 同理,不再需要Combo,不利于缓存 |
这个网址可以帮我们直观的对比http2和http1.1的区别https://http2.akamai.com/demo
我们的目标
今年已经是2021年了,周围的Web服务器基本都是https2了,看到这里,就有读者会好奇的问题了,难道仅仅从http1升级到2就是我们希望的全部了?
答案肯定是:No!
这里再回到Web诞生的元年,看看我们的起源故事
最早期互联网只有Telnet,当时还是一个CS模型,为了减少C端的笨重(你得需要一个专门的telnet软件),交互验证的繁琐(各种加密校验),给单调的内容加点色彩(ASCII字符的打印界面变得更加多媒体和彩色),于是跨系统的浏览器被开发出来了,为了让浏览器识别的内容更通用,HTML富文本协议被确认了,BS模型才被正式提出。
到了如今的年代,技术的多样性,性能的瓶颈都被大型在线多人游戏这些场景一遍又一遍的挑战着技术的极限。而我们的传统Web技术还受制于各种安全/隐私的困扰,导致很多CS早已成熟的技术,望而不能用。
就比如说对于网络请求的全链路来说,客户端开发的同学就可以有能力访问到更底层的api,来获取更原始的信息,同时对于底层api的控制权限更大,而传统的浏览器端的同学,就不得不面对一层又一层被包裹的api,忍受着前端就只是平面展示和表单填写这些低性能场景的歧视,而传统的客户端能力才是更丰富多彩的舞台的偏见。
如果以上言论放在9012年,我们还可以自嘲一句,可是现在flash已死,fuchsia横空出世,flutter web都已经可以基于skia来实现统一端能力的今天,我们为什么不能直接继承CS社区的超能力,直接把失去的二十年一股脑的补回来呢?
就单单从通信层面,从QQ时代就已经非常成熟的UDP通信技术,无论是传输速度,弱网通信的可靠性,都甩现在传统的TCP一大截的多产品证明的技术,是否就可以复刻在现在的浏览器通信场景上?这就是我们的目标
Why UDP
让我们再把时光机切到当年,回顾一下QQ当年为啥要选择UDP而不是TCP作为通信的主要载体,首先我们知道QQ腾飞的那几年刚好是我们桌面刚起步,大家还是用小猫上网的年代,然后接着就是辉煌的移动互联网红利10年,无论是早期的个人家用机还是移动互联网,离不开的就是带宽窄,网速慢,不稳定,容易丢包和断连。而TCP无法感知网络中断这些问题。。。这个算是TCP一个容易踩的坑,但这并不能说明UDP就比TCP好(或者说解释为何要使用UDP)。因为在UDP上面一样需要面对这些问题,而解决这类问题的方法和在TCP上面进行应用层心跳的方法其实没有本质上的区别。
最本质上UDP的优势还是带宽的利用。以前的网络抖动可能是多方面的,例如延时突发性地暴增、也有可能是由于路由层面的变化突然导致路由黑洞,还有各种等等等等的问题。TCP因为拥塞控制、保证有序等原因,在这种网络状态上对带宽的利用是非常低的。而且因为网络抖动的原因,应用层心跳超时(一般不依靠keepalive)或者应用层主动断掉socket之后TCP需要三次握手才能重新建立链接,一旦出现频繁的小抖动就会使得带宽利用更低。而等待四次挥手的时间,也会占用服务器上宝贵的资源。总结来说,当网络差到一定程度了,TCP的优势反而会成为劣势。
这时候我们再看看UDP在这种情况下的表现。使用UDP对抗网络抖动,说到底就是在应用层比TCP更快地探测和重传,一旦超过一定的时间没有收到回复,客户端可以选择马上重试或者换一个IP:PORT重试(假如你的服务像QQ一样有多个接入),在服务器端则可以果断地断掉socket。而可以应用UDP的时候,往往是你的应用层协议本身已经具备了一定的面向连接的特性。如果你应用层的协议已经达到了一定程度的消息幂等,客户端可以几乎无脑地进行重传,这样就可以尽可能地降低网络抖动的影响,同时也可以尽可能地利用整个带宽。而刚好QQ的协议,就具备类似的特点。简单来说就是我们可以使用UDP实现一个面向连接协议,这个协议可以很好地适应当时的网络状况和QQ本身的业务。但凡事都有成本,成本就是你的应用层协议本身需要去实现抵抗网络异常带来的问题。例如乱序、例如业务数据的分片和重组、例如网络状态探测等等等等。。。而现在UDP也应用在很多跨运营商、跨地域、跨机房之间的服务调用当中。原因无它,就是网络烂到一定程度了。
而换做我们现在传统电商型浏览器应用场景下,我们对于信息的对等安全加密以及校验要求其实没有那么高,本质上来说,只是简单的拉取服务器信息,并基于表单交互的方式收集用户信息上传至服务器,即使失败了,直接原包重试即可,也没有利用比如断点续传,差分合并等等对于内容处理的高级需求。那么基于TCP协议上的http协议的很多特性,其实在简单场景下其实就是多余的。但是因为设计之初,是为了满足普遍场景下的更广泛而全面,以及更专业化的需求,所以整个应用层协议是大而全的。对于极致性能的比如数据交互场景,以及国内广泛的电商,ToB私有化需求来说,哪怕是http2都显得巨大且臃肿。
说说鸿蒙
鸿蒙也是最近一年,社区和知乎吵得最火热的话题了。无论从PPT的前瞻性,还是从这个开源生态的资源扶持程度,华为的同学都表现出了对于未来从端出发,回端上去的匠人精神。我也有幸从早期的快应用联盟,到华为的卡片服务,信息流开放平台,再到现在的鸿蒙开发者社区开放平台,一直从头到尾参与过具体的开发工作。其中的艰辛,外人难得其精髓,只有经历的人才知其个中滋味。“万物互联”这个概念,说了差不多也有快十年了,只有华为真正意义的实现了,把广大的浏览器开发者生态,纳入到了鸿蒙大家庭,给了我们广大前端开发同学更大的场景,更开阔的市场,更底层的能力,更亲近设备的体验。
从1997年第一次做个人网页开始,曾想着尝试把操作系统复刻到浏览器上。后来ChromeOS圆了我第一个梦,我就又开始做梦,能否在浏览器上控制空调,电扇,门铃,帮我烧菜,洗衣服做饭。现在这个梦想也实现了,通过鸿蒙,前端开发者可以通过熟悉的开发方式以及流程,轻松复刻现有的项目能力,以及复用生态,同时基于鸿蒙提供的更底层的分布式计算能力和通信能力,让我们摆脱了传统浏览器的舒服,让更多的能力能在真实场景下得到释放。
而且,鸿蒙作为一个华为自我知识产权的系统,通过之前多次和他们核心开发组的沟通,不管是对于问题的解决速度,以及专业的态度,对于扩大整个鸿蒙生态圈,和吸引更多优秀的前端行业从业人员跑步入场,和口碑相传,都是释放了非常积极的信号。
我们有理由相信,在整个大前端生态下,鸿蒙未来必然会成为特定领域的攻坚利器,为从端出发,回端上去的理想去画上一段优美的旋律。
未来已来
回到我们现在的浏览器,如果我们可以定制应用层协议,面向更底层传输层,做合适选型,做最精简的内容控制,在配合现在越来越强劲的端计算能力,加上P2P技术优势,我们有理由相信,我们可以把整个链路层在大宗客户的应用场景下,平均提速2倍以上。
其实鸿蒙ppt也提出了分布式设备互联的概念,只是他把底层p2p技术,数据交互,云计算(端计算),hack到了操作系统层面,当然我们不需要去做一个分布式操作系统,基于2021年已有的浏览器API,我们就已经可以实现同样的能力。
WebSocket协议在2011年由IETF标准化为RFC 6455,后由RFC 7936补充规范。 Web IDL中的WebSocket API由W3C标准化,到现在已经被各大主流浏览器,包括移动端内核广泛支持了。
通过WebSocket让现代浏览器可以脱离传统的httpRequest请求,来自己控制通信节奏,建连,传输,重试,断开。第一次让BS开发选手可以选型类似CS已经常用的TCP通信。
而有了通信的第一步之后,就要考虑对于内容信息的加密和序列化,通过WASM技术,我们可以复用J2EE社区相关的内容序列化技术,比如protobuf,实现前后端通信同构。
借助以上技术,我们可以就可以直接基于tcp的websocket直接和后端server通信,抛弃传统的文本传输,直接发送和接受二进制流。如果想提速文本到二进制流的序列化速度,还可以通过WASM来调用C函数来实现加速。
WebRTC是2011年提出的,本意是用在浏览器端,通过提供简单的javascriptAPI就可以达到实时通讯(Real-Time Communications (RTC))能力,也就是P2P,端到端通信。这里的端可以是浏览器到浏览器,也可以是浏览器到后端Server。这里,我们比较看重的WebRTC的通信是可以基于UDP的,而我们的场景,其实并不需要音频或者视频流,我们需要一个能自定义数据的通信方式,而WebRTC提供的RTCDataChannel恰好满足我们的需求,一个基于UDP协议的类socket数据通信通道。另外WebRTC是可以实现基于浏览器的伪“万物互联”的,基于万物互联,我们甚至可以实现p4p(https://en.wikipedia.org/wiki/Proactive_network_provider_participation_for_P2P)的云计算。
但是WebRTC在BS通信时候,有一些繁琐,他原本设计是基于P2P的,所以在建连的时候,需要提供 STUN、ICE 和 TURN 来支持 NAT 穿透。而如果是基于BS模型,这些是可以省略的,不过在github上已经有很多人阉割了这部分来实现对于基于WebRTC的BS通信,比如netcode.io。
另外谷歌也在积极的推广基于QUIC协议的udp通信,他使用WebTransport,也是WebRTC体系下的一套浏览器API,提供低延迟,client和server之间双向通信的能力。 核心的能力点包括:
- WebTransport 提供基于QUIC 和 HTTP3实现的API, 自动获得QUIC和HTTP3本身的特性,比如应用层的拥塞,避免队头阻塞。
<!---->
- 双向通信的能力,多个传输通道复用一个连接的能力,能够很好的替代WebSocket。
<!---->
- 提供发送/接受不可靠UDP的能力,这个是浏览器一直欠缺的能力。
<!---->
- 对比WebSocket基于消息的模型不同,WebTransport是基于流的,在传递消息的时候需要自己再增加一层数据的封装格式。
另外,在最近的google IO大会上,基于SW+WASM技术提出的WebContainer模型(在浏览器上运行原生的NodeJS应用),让端计算能力再次放大,起到了里程碑式的突破,对于前端开发人员,可以在自己的浏览器里,直接跑标准的nodejs服务应用。实现真正意义的offline APP,离线浏览。
而配合这一技术的是ServiceWorker技术,是google在2016 年Google I/O 大会提出,于2017年落地的,旨在增强Web 体验,缩小Web App 与Native App 的差距,并创建类似的用户体验的技术,最大的特点是能拦截客户端请求,可编程式的控制输出,模拟文件IO的阻塞,配合WASM可以编译node/deno/go 的模块,让基于浏览器的单端计算能力有更大的想象空间。
最后的最后,再说一个好消息!就在最近,QUIC协议正式成为了官方的IETF RFC。相信不久的将来,浏览器应用开发,可以直接照搬客户端社区多年以来的宝贵经验,在数据通信的优化领域,一路长虹!
未来已来!需求快排期!
参考文献:
https://www.yhspy.com/2017/04...
https://zhuanlan.zhihu.com/p/...
https://segmentfault.com/a/11...
https://www.infoq.com/article...
http://www.alloyteam.com/2017...
https://www.yuque.com/kshare/...
https://zhuanlan.zhihu.com/p/...
The End
如果你觉得这篇文章对你有帮助,有启发,我想请你帮我2个小忙:
1、点个「赞」,让更多的人也能看到这篇文章内容;
2、关注公众号「豆皮范儿」,公众号后台回复「加群」 加入我们一起学习;
关注公众号的福利持续更新,公众号后台送学习资料:
1、公众号后台回复「vis」,还可以获取更多可视化免费学习资料。
2、公众号后台回复「webgl」,还可以获取webgl免费学习资料。
3、公众号后台回复「算法」,还可以获取算法的学习资料。
4、公众号后台回复「招聘」,获取各种内推。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。