要强的 TCP

741 阅读8分钟

圆桌会议

“又到了一月一次的总结大会,大家说说最近工作上出现的问题吧。” 浏览器老大坐在圆桌最前端,说道:“好意见赶紧提,吐槽也可以说。”

“我!我!我!” TCP 喊道。

“嗯,你说。”

“上次你们优化请求,怎么没叫上我呢?对于请求速度优化我也有点想法!” TCP 说道。

“上次啊,我其实也就是到 HTTP 那儿抱怨了一句,主要还是 HTTP 和他服务器的朋友一起弄好的。” 浏览器老大看了 HTTP 一眼,眼中满是得意,转头看向 TCP 说道:“说说你的想法吧。”

“上次你们弄缓存时,我也仔细的查看了一下通过我发送的报文,总结出来一个规律。” 说着 TCP 拿出了事先准备好的一张图,并投到了圆桌对面的屏幕上。

“大家请看,这是用户打开 https://blog.acohome.cn 时,通过我发送的一系类请求,我按域名进行了分类。” 说着 TCP 站了起来,走到屏幕前,指了指,屏幕上有如下一个表格

域名 请求数
blog.acohome.cn 33
cdn.bootcss.com 4
code.jquery.com 1
hm.baidu.com 4

“大家请看,该页面内的资源来自 4 个服务器,却有 42 个请求。老大,目前我们实现 TCP 通道的策略是针对单个 HTTP 报文的,换句话说,就是每个 HTTP 请求,都会开启一个 TCP 通道,对吧?”

“嗯,没错。因为 HTTP 是无状态的。” 浏览器老大陷入了沉思,貌似知道了 TCP 所要阐述的内容了。

“一个 TCP 数据通道的建立和释放,需要进行三次握手和四次挥手。针对于上面的情况,需要进行 42 次这样的过程,但是这其中有很多次是发生在同一个客户端和服务端的。比如我需要与 blog.acohome.cn 所在的服务器进行 33 次相同数据通道的建立与释放,这会照成极大的资源浪费。以至于我在服务器的哥们对我有了不少的抱怨!”

“那说说你的解决方案吧!” 浏览器老大意识到了问题的严重性,确实这一块有较大的资源浪费。

长连接

“解决的方案其实简单,我不把数据通道销毁就好了,也就是长连接,针对相同域名,我每个单独建立一个通道,当然在请求多的情况下可以建多个。” TCP 自信满满的说道。

“嗯!不错的想法” 浏览器老大闭眼思考了一会,说道:“但是不能泛泛而谈,也有可能会出现需要及时释放的情况。这块容我想想该怎么实现!”

“嗯,确实会有特殊情况发生,需要好好考虑下。” TCP 也思考了一下。

“这样吧,我仔细想想!然后给你一个解决方案。” 说着浏览器老大站起身,说道:“那今天的会议先到这!”

keep-alive

大概过了 500ms 的样子,浏览器老大来到 TCP 的办公室。

“你提的确实是个好想法,但是是否保持数据通道不该由你来定,应该由上层的调用者来确定,就是 HTTP,我顺便把他也叫过来了,你们商量商量。” 说着 HTTP 穿着白大褂走了进来。

“刚刚我听了,很不错的想法。老大,TCP 说的没错,我们这儿的请求基本上都是需要长连接的。要不就都这么处理吧!” HTTP 转向浏览器老大说道。

“不不不,你忘了上次我们主动加缓存出的问题了吗?如果开发者不需要长连接就麻烦了,这里不能写死!你接触开发者,所以在你的报文里加个特殊的字段吧,标志这个请求所在的域名是否需要长连接,然后把这个设置开放给开发者吧!” 浏览器老大想起了前几天定死缓存时发生的事情,慎重的说道。

“确实哈,那就和之前加缓存头一样?” HTTP 略微有点尴尬。

“对,字段就叫 Connection 吧,如果是 keep-alive 就是长连,如果是 close 就不长连,默认是长连,这样 OK 吧?”

“没问题!” TCPHTTP 同时说道。

“那我顺便把这个字段提到 HTTP 1.1 规范中吧,刚好规范大会最近要开。” HTTP 补充道。

“嗯,让大家都用上也好 ~” 浏览器老大得意的说道,边说,边走了出去。

哎呦,不错哦

TCP 牛啤啊!” 说着 HTTP 搂上了 TCP 的肩膀。

“那当然,风头不能都让你占了啊!”

“长连是好想法,但是请求的顺序不能乱哦,我给你一个报文,你要返给我一个,不然我可不知道怎么对应的!”

“哎 ~” TCP 叹了口气。

“怎么了?不能保证?” HTTP 显的有点慌乱。

“不是啊,我是能保证,但需要你一个一个给。”

“对啊,现在就是这样啊!你叹什么气?”

“其实把,我有更好的方案,要不你了解了解?”

“真的?还有更好的?” HTTP 有点兴奋的说道。

管道化

“是有啊,但是和我服务的哥们讨论下来,实际的可行性可能有点问题。” TCP 面露尴尬。

“试试嘛,没准能行呢?”

“那我先说说实现原理哈。”

“嗯,你说!”

“首先,你直接把请求按顺序的给我,不需要等我给你的响应,直接把所有的请求按顺序给我就好,然后我把请求按顺序给到服务端,服务端在按顺序返回给我!” 说着 TCP 拿起纸笔画了起来。

三种实现请求的方式对比

“你看哈,最左边是现有的方式,中间是加了 keep-alive 的实现,右边呢是我刚提到的想法,我把最后这种实现叫做管道化,你看看。”

“管道化,有意思!牛皮啊!你给解释解释呗!我看你画的有点复杂。” HTTP 皱起了眉头。

“你把请求一开始就按顺序给我,由于通道是双向的,在建立连接后,我就可以边发送数据边接收数据,也就是说在第一个请求发出去后,我就可以开始接收第一个请求的数据了,也就是图中响应和请求出现交叉的地方。”

“原来你还有同时发送和接收的能力啊!确实,如果能实现的话,是够快的,那么问题在哪?”

“如果考虑到网络状况的话,问题就出现了!你想想如果说我刚发完所有的请求,但通道由于网络的原因,奔溃了!会发生什么情况?”

“按照正常处理就行啊,重建通道,再次发生!”

“问题就出在这!那第一个请求的响应怎么办?如果这个请求是 POST 请求,不就发了两次吗?要是这次多余的 POST 请求出了状况,那责任谁担?”

emmmm

“还有就是即使网络状况好,就像刚刚画的,由于你给的请求是顺序的来,然后我顺序的发出去,那响应就是固定的顺序了,如果说第一个请求的响应服务器需要处理很长的时间,那后面的请求不也顺带着变慢了!”

emmmm

“还不如我另开一个 TCP 通道来的快!”

emmmm

“所以这个其实仅在特定的条件下,才会有效果,不然还不如不用!并且管道化单方面支持还不行,必须要和我服务器的哥们一起才行!”

“经你这么一说,确实啊,实际使用情况有限。兄弟,其实在设计缓存那一块时,我发现了一个真理!”

“什么真理?”

“把权限交给开发者!”

“什么意思?”

“我们这边其实并不能真正了解开发者的意图,因此能做的也有限,但如果你把是否使用管道化的权限交给开发者,在开发者确认能使用的情况下,就使用,这样就能在一个安全稳定的环境下,对性能进行提升了!”

“对哦,既然这个并不能针对到所有的情况,那么交给页面开发者就好了!真是个好想法!”

“还有你并不能直接接触开发者,那还是由我来给你吧。我把你的想法一并提到 HTTP 1.1 规范中,够意思吧!”

“好兄弟!”

算了,关了吧!

屏幕外:

“听说 HTTP 1.1 这次增加了一个叫管道化的技术,你了解过吗?”

“嗯,听说过,但网上对这个技术褒贬不一啊,并不能解决真正实际的问题。”

“怎么开呢?我想试试!”

Chrome 下有个设置,你打开就行,你看在这!”

“嗯,打开了!”

“后端收到请求确实快了,一下子来这么多????”

“你有对你的服务器做过相关优化吗?”

“没有啊!”

“那还不如不开,你多试几次,看看是不是真的有提升?”

脚本一开,一顿操作,自动化测了 1000 次。

emmmm 好像还不如不开...”

“...”

“算了,关了吧。”

相关阅读