TCP吞吐性能优化的吐槽与拯救
上周文章转发朋友圈后,我补充了个评论,我不晓得为什么RDMA底层传输协议还在复用TCP那一套,只是为了重用而重用吗?完全可以重写的协议还在GBN,还在将SACK作为GBN的优化,沿着老路重走一遍…请用QUIC吧,但这话不能对RDMA说,它用不了QUIC,转念一想,广域网传输也不是非用QUIC替代TCP,剥离导致TCP性能缺陷的因素,学着QUIC的样子改掉它,依然留着TCP的躯壳,作为TCP-ng,岂不是鸠占鹊巢更美妙?抛砖引玉,写下本文。
TCP吞吐性能已经经不住优化了,因为它的固有缺陷来自于它的原始设计。
TCP术语太多,滑动窗口,慢启动,拥塞避免,快速重传,快速恢复,超时重传,几乎任何资料都不可避免地以上述术语为线索,TCP的复杂性,无论是协议还是实现,均来自于此。
如此复杂的协议,竟然仅仅可用(幸亏AIMD也能让它收敛),毫无效率可言,够了!
最初的设计中,TCP只有积累确认,为让TCP滑动窗口持续滑动,必须以dupACK识别丢包,然后GBN(go-back-N),其中N指示重传的数量,这是一个简洁的设计,但低效。TCP原始设计的状态机为:
以Linux TCP实现为例,关于这个状态机的各状态描述如下:
/* Linux NewReno/SACK/ECN state machine. * -------------------------------------- * * "Open" Normal state, no dubious events, fast path. * "Disorder" In all the respects it is "Open", * but requires a bit more attention. It is entered when * we see some SACKs or dupacks. It is split of "Open" * mainly to move some processing from fast path to slow one. * "CWR" CWND was reduced due to some Congestion Notification event. * It can be ECN, ICMP source quench, local device congestion. * "Recovery" CWND was reduced, we are fast-retransmitting. * "Loss" CWND was reduced due to RTO timeout or SACK reneging. ... */
自SACK被引入直到RACK/BBR,围绕TCP原始设计的优化终于再也无法满足需求,并越来越成为吞吐性能提升的障碍。成为性能提升阻碍的原因不外乎:
- 状态机依然根据原始设计转换,在非Open状态接管拥塞控制算法。
是时候拿掉这个状态机了,就像赶走牵引蒸汽机的马匹一样。
为此需要设计一个新协议。但evolution比revolution更容易被接受。新协议不是QUIC,而是TCP-ng。重用TCP协议头是它的最大特征。
大致介绍一下TCP-ng对TCP做了哪些扩展以及如何做的。
首先,将TCP协议中不合时宜的机制剥离,如果不晓得哪些是不合时宜的,看看QUIC的方式:
- 收端不再支持reneging,内存已不再是瓶颈,手机内存动辄10G+。
- SACK段数量不再受限,带宽已足够,不必再压缩协议头空间。
- end-to-end越来越普遍得使用TLV而不再青睐定长的短协议头。
以上措施之外,需要增加的新机制:
- 以发送顺序而不是序列号标识传输,分离end-to-end和传输逻辑。
- 完备的不依赖TCP拥塞状态机的拥塞控制算法。
综上,TCP-ng需要做的是:
- 取消reneging。QUIC不允许reneging。
- 完全的SACK。QUIC即完全SACK。
- 独立的传输。QUIC中的packet ID
- 解放拥塞控制算法。
幸运的是,以Linux TCP为例,需要修改的地方非常少,分别来看就是:
- 收端和发端删除reneging相关的代码。
- 收端将所有SACK段放入payload。发端用payload解析出的SACK段扫描retrans rbtree。
- 发送端实现完整的RACK,发送时不区分new data和retrans。
- 取消拥塞状态机,cc算法根据采集带宽来自己决策。
取消reneging的风险是,乱序太多或RTT太抖动的弱网环境,收端内存占用会很大,空洞长时间无法填补,严重情况下会出现类DDoS症状。
取消拥塞状态机,TCP的复杂性就砍掉了大部分,背后的思想很简单:
- 拥塞一定会带来测量吞吐及RTT的变化,与丢包无关。
- 丢包一定可以靠RACK重传恢复。如下图:
Linux TCP实现了RACK之后,其实已经很完美了,启用RACK后,完全依赖RACK来mark lost,而不再使用oneshot的tcp_update_scoreboard,这表明Linux RACK-Based TCP已经完全基于时间序发包了,此前的scoreboard已经成了历史。
核心问题SACK段数量受限以及时间序发送问题已经解决,TCP-ng便得以快速滑动窗口,HoL得到极大缓解,剩下的就是细枝末节了。
TCP-ng的不足是,ACK依然无法和transmit相对应,造成DSACK误判,undo效率降低。
以Linux TCP实现为例,当前的undo操作依赖于:
tp->undo_retrans == 0
该字段在收到DSACK时递减。由于无法区分一次重传报文带来的DSACK还是两次重传中其中一次带来的DSACK,有可能会造成undo失败并影响重传效率:
- 进入重传状态后,只要之前有丢失的重传依然未决,本次undo就会失败。
- retrans_out占据inflight份额,重传窗口无法张开。
好在packets_out是准的,下一轮packets_out清零之时可顺便清除retrans_out,但迟到的DSACK会带来warning。所以我建议在RTO时无条件清除retrans_out。
彻底解决这个问题需要增加类似QUIC的packet id字段。为此,TCP-ng2我这么建议:
- 将SACK option分裂为3个type:type1为packet number(8字节,必选); type2为SACK(in payload);type3为stream ID(4字节,可优化接收负载均衡,或自研网卡优化RSS)
- ACK中SACK段格式为begin data-seq:begin packet-seq~end data-seq:end packet-seq的映射段,用于发端检测并对应DSACK。
- data seq和packet seq全部采用字节单位,不影响TSO/GSO,GRO/LRO等offload机制的运行。
undo只是降低一些重传率,对整体的带宽利用率提高影响有限,且机制复杂,为了不引入更多复杂性从而引发额外问题,不建议实现undo。
…
TCP-ng重用TCP协议头及其大多数原有处理逻辑,其原因是:
- 主观原因:我太懒惰且编程水平太差,重写一个新协议虽好看但beyond my ability。
- 客观原因:TCP-ng兼容性极好。
TCP有诸多缺陷,设计一个针对性弥补这些缺陷的新协议并不是一件很难的事,但在工程和生态视角,将新协议部署落地就太难了,一个很简单的例子就是IPv6。
IPv4的问题众所周知,IPv6逐一针对性解决,可是IPv6的部署之路却很艰难。
与之类似,QUIC也一样。
妇孺皆知的是QUIC对状态防火墙不友好。何止状态防火墙,QUIC对所有途径的运营商设备都不友好:
- UDP没有状态机,设备无法跟踪连接,便无法精确实施ACL,计费等关键动作。
- QUIC over UDP,作为应用层协议迄今没有统一标准,且变化多端,设备商的固化升级成本太高。
- QUIC协议并非没有生态,而是各大厂商自立门户,生态太多,无法协调。
- QUIC用户态的cc算法很容易被不懂拥塞控制为何物的人乱搞,严重破坏互联网公平。
…
所以,我选择不修改TCP协议头的方式,所有的设备还是和原来一样运作,新协议完全透明经过。再也没有人说新协议不友好了。
下面是我之前写的一篇欺骗网络设备的内核插件,它可以将UDP假装成TCP:https://github.com/marywangran/pseudotcp-tunnel
最后,演示一个扩展TCP协议的实际例子。
当前主机性能相比1980年代已经大幅提升,进程容量大大增加,为提升主机搜索可用端口号的效率(connect/bind中的逻辑),需要扩展TCP端口号位宽。下面介绍如何不改变TCP协议头而将端口号扩展成24位:
- 新端口号的低16位用TCP协议头中的端口号字段表示。
- 新端口号的高8位用TCP协议头中2个新的8位自定义option表示。
- 发送端自行拼接端口号组装数据包。
- 接收端解封装端口号多路复用。
浙江温州皮鞋湿,下雨进水不会胖。
TCP吞吐性能优化的吐槽与拯救相关推荐
- Web 性能优化:HTTP
个人博客 在策略层面,(四维空间针对指定问题绝对复杂性的)普适优化手段最终都是在两种维度之间进行取舍转换: 当时间更宝贵时,用空间换时间: 当空间更宝贵时,用时间换空间. Web 性能优化:TCP W ...
- TCP接收端优化吞吐性能的把戏
惯常TCP吞吐优化均在发送端激进传输,比如一个报文发两遍,盲目扩大cwnd此类.将这种动作称作"猛推",就必然存在是"猛拉",即在接收端做一些把戏. 还有一天放 ...
- 记一次单机系统的性能优化:最后竟是 TCP 的锅
前言 这篇文章的主题是记录一次 Python 程序的性能优化,在优化的过程中遇到的问题,以及如何去解决的.为大家提供一个优化的思路,首先要声明的一点是,我的方式不是唯一的,大家在性能优化之路上遇到的问 ...
- 如何进行TCP性能优化
本次和大家聊一下TCP性能优化. TCP全称为Transmission Control Protocol,每一个IT人士对TCP都有一定了解.TCP协议属于底层协议,对于大部分研发人员来说,这是透明的 ...
- 拯救你的年底 KPI:前端性能优化
前言 性能优化 ,每个工程师跑不掉的一个话题.这里是本人总结的一些优化手法,希望对大家有所帮助,感谢各位大哥???? 演示demo 在线预览: http://118.25.49.69:8085/ fe ...
- 何崚谈阿里巴巴前端性能优化最佳实践
转载:http://www.infoq.com/cn/interviews/hl-alibaba-front-end-performance-optimization 大家好,我现在在阿里巴巴园区采访 ...
- lwip协议栈优化_干货分享 | KNI性能优化实践
友情提示:全文5000多文字,预计阅读时间15分钟 文章源自现网实践对支撑及用户态/内核态网络报文交换场景的认识,欢迎有Linux/FreeBSD内核.网络协议栈.DPDK优化实践经验的同学留言探讨- ...
- 43 | 套路篇:网络性能优化的几个思路(上)
上一节,我们了解了 NAT(网络地址转换)的原理,学会了如何排查 NAT 带来的性能问题,最后还总结了 NAT 性能优化的基本思路.我先带你简单回顾一下. NAT 基于 Linux 内核的连接跟踪机制 ...
- Linux问题分析与性能优化
目录 排查顺序 方法论 性能分析工具 CPU分析思路 内存分析思路 IO分析思路 网络分析思路 基准测试工具 参考 排查顺序 整体情况: top/htop/atop命令查看进程/线程.CPU.内存使用 ...
最新文章
- handler消息机制入门
- 从认证到调度,K8s 集群上运行的小程序到底经历了什么?
- jsp项目在idea需要导入什么依赖_Java开发工具IntelliJ IDEA配置项目系列教程(五):模块依赖...
- python flag函数_Python(函数基础,阶段总联系)
- 文档模式引起的浏览器兼容问题
- 基于linux智能家居系统设计,基于Linux的智能家居的设计(2)
- 一文梳理多任务学习(MMoE/PLE/DUPN/ESSM等)
- 谁来执行Rebalance以及管理consumer的group呢?
- MarkDown学习指南(一)
- 作为软件工程师,如何进行知识管理
- 简单了解cms(内容管理系统)
- java实现psd格式图片读入
- chrome浏览器加载图片失败问题
- qq邮箱mx服务器,QQ域名邮箱管理系统MX记录是什么?怎么添加设置?
- 物联网 DFrobot 掌控版的使用 智能灯和MQTT
- 单独上线音乐直播APP,“LOOK直播”能给网易云音乐带来什么?
- 浙江大学-翁凯 C语言进阶,编程题
- 电路中的VCC是什么意思?
- 电气EPlan软件第一章到第五章的学习
- 基于EasyDarwin开源流媒体服务器框架实现EasyNVR H5无插件直播流媒体服务器方案