一, TCP 知识体系
我们从三个维度去分析服务器开发的 TCP 知识体系, 分别为性能法则, 设计法则和避坑法则.
二, 性能法则
性能法则大致总结如下:
1. 减少数据传递
下面引用了左耳朵的 "程序员如何用技术变现" 文章中的一部分:
从上面我们可以看出减少数据传递对于性能是非常重要的.
2. 根据场景设置 MTU
如果是内网应用, 通过合理设置 MTU 来提升性能是不能忽视的一种手段; 对于移动应用, 一般可以设置 MTU 为 1492; 对于外网应用, 则设置通用的 1500.
3. 利用 TCP offload
带宽消耗高的应用, 可以考虑利用 TCP offload 来提升性能.
4. TCP NODELAY
目前服务器程序一般建议设置 NODELAY 为 true, 如果需要对小数据包合并, 则可以考虑在应用层做数据合并 (参考下图 Wikipedia 中内容).
详细内容请参考:"https://en.wikipedia.org/wiki/Nagle's_algorithm"
5. 采用合适的拥塞控制算法
下图展示了数据包经过路由器 Queue 的场景.
第一种是最理想的情况, 数据包到达路由器, 无需等待就能直接转发出去; 第二种是等待一段时间, 才能发送出去; 第三种是因为路由器 queue 满, 数据包被路由器丢掉.
发送数据过猛可能导致第三种情况发生.
下面展示了 Linux 默认算法 CUBIC 和 BBR 算法在丢包情况下的吞吐量对比:
从上图可以看出, BBR 拥塞控制算法可以在 20% 丢包率以下保持吞吐量, 因此 BBR 的抗网络抖动性比 CUBIC 要好.
BBR 算法优异的根本原因如下:
在有一定丢包率的网络链路上充分利用带宽
降低路由器的 queue 占用率, 从而降低延迟
一般建议在非网络拥塞导致丢包的场合使用 BBR 算法, 例如移动应用.
对于带宽比较大, RTT 时间比较长的应用场景, 可以参考.
6. 使用 REUSEPORT
针对短连接应用 (例如 PHP 应用), 为防止服务器应用来不及接收连接请求, 可以采用 Linux REUSEPORT 机制. 我们开发的数据库中间件 Cetus 利用 REUSEPORT 机制成功避开了应用短连接的冲击.
三, 设计法则
1. 规避 TCP HOL 问题
尽量采用多连接, 不要采用单个连接来传递大量数据.
2. 传输尽量平稳, 不抖动
如果数据传输比较抖动, 那么容易导致如下问题:
内存膨胀
性能不稳定
压缩算法效率低下
在开发数据库中间件 Cetus 的时候, 我们控制了每次数据传输的传输量, 在采用同样压缩算法的情况下, cetus 压缩比远远好于 MySQL 的压缩比.
3. TCP stream 流式传输
TCP stream 主要用在中间件服务.
下图是没有采用 TCP stream 的交互图. 中间件接收完 Server 端的响应后, 才开始发送给客户端. 不少数据库中间件采用这样的工作方式, 导致中间件内存消耗巨大.
下图采用了 TCP stream 方式后, 不仅降低了延迟, 也降低了内存消耗 (因为无需保留所有响应).
服务器中间件程序最好实现 TCP stream, 否则易发生内存炸裂等问题.
4. 上层应用 pipeline 机制
TCP 本身并不具备 pipeline 机制, 但上层应用可以利用 pineline 机制来提升服务器应用的吞吐量.
下图是没有采用 pipeline 的交互图, 客户端需接收到服务器响应后才能发送下一个请求.
下图是采用 pipeline 的交互图. 客户端无需等待响应就可以连续发送多个请求.
对于 TCP 来说, 请求 1, 请求 2 和请求 3 看成一个请求, 响应 1, 响应 2 和响应 3 看成一个响应; 对于上层应用来说, 则是 3 个请求, 3 个响应.
目前, 很多协议或者软件采用 pipeline 机制来提升应用的吞吐量, 例如 HTTP v2 协议支持 pipeline 发送请求, Redis 采用 pipeline 机制来提升应用的吞吐量.
5. 合并小数据
运行 TCPCopy 的时候, intercept 返回响应包的 TCP/IP header 给 tcpcopy. 一般 TCP/IP header 只有几十字节, 如果每次 write 操作只传输一个响应包的 TCP/IP header, 那么效率就会非常低. 为了提升传输效率, intercept 合并若干个响应包的 TCP/IP header 信息一起发送.
四, 避坑法则 4.1 加上 keepalive 机制
TCP keepalive 机制可以用来检测连接是否还存活, 具体可以参考 "对付 Reset 流氓干扰: TCP keepalive".
1. MTU
参考:"https://wiki.archlinux.org/index.php/Jumbo_frames"
2. 确保网络通畅
云环境, 中途设备程序, TCP offload 和负载均衡器或多或少存在一些问题, 而这些问题如果不及时解决, 会极大影响程序的性能和问题排查.
这方面一般可以通过抓包的方式去查明问题.
下面展示了负载均衡器自身 bug 导致了网络不通畅.
由于负载均衡器没有严格按照 TCP session 的方式进行负载均衡, 有些 TCP session 的数据包跑到了不同的机器, 进而导致应用端报请求超时.
最初连接的数据包跑到了 180 机器.
后来这个连接的数据包跑到了 176 机器 (参考下图).
负载均衡器出现这种 bug, 会造成用户的极大困扰, 很难查明问题原因.
这时要么更换负载均衡器, 要么找厂商解决负载均衡器的 bug, 否则上层应用会一直报网络超时等问题.
五, 总结
对于服务器开发人员, 只有了解了 TCP 知识体系后, 开发起来才能够得心应手, 同时可以规避一些潜在的坑.
来源: http://network.51cto.com/art/201909/602551.htm