当前位置: 首页 > news >正文

“小坝” 策略:始发站 buffer 控制与优化

端到端,这两个端是两个应用程序中的位置,第一个端指数据被产生处,第二个端指数据被消费处。更一般的,把数据发生的应用程序所在的主机视为数据始发站也是合理的。

网络中遍布 buffer,buffer 却是一把双刃剑的存在,它能吸收统计突发降低丢包率,但塞得太满也会带来高延时,如何配置 buffer 的大小以及如何控制 buffer 的用量一直都是网络传输领域的核心之一(另一个核心关乎拓扑)。

bufferbloat 就是一种 buffer 使用不当带来的高延时症状,但这种使用不当是无心错,因为 buffer 总有一种被填满的倾向,buffer 被填满似乎是一种博弈均衡的结果,唯一的措施几乎就是不要把 buffer 配置太大(当然,很多人不会同意)。

但在始发站,针对始发站的 buffer,却可以严格控制 buffer,避免 bufferbloat。

始发站的网络传输逻辑从 socket write 开始,在网卡 xmit 后结束,期间经过两个 buffer,一个在 socket 和 TCP(UDP 也一样) 之间,另一个在协议栈和网卡之间(比如 Qdisc),或许网卡本身还有个 buffer,但它不通用,故忽略。

确保这两个 buffer 保持低用量,即可确保始发站不会 bufferbloat。

以 Linux 协议栈为例,socket 写入 TCP 发送队列前有机制去检查当前发送队列的长度,当它大于一个阈值,意味着不能再继续写入,直到队列长度低于该阈值,该机制可通过 net.ipv4.tcp_notsent_lowat 配置起作用。比如将它配置成一个比较低的值,具体多少关乎第二个 buffer。该机制检测的代码如下:

static inline bool tcp_stream_memory_free(const struct sock *sk)
{const struct tcp_sock *tp = tcp_sk(sk);u32 notsent_bytes = READ_ONCE(tp->write_seq) - tp->snd_nxt;return notsent_bytes < tcp_notsent_lowat(tp);
}

TCP 为避免本地队列(比如 Qdisc)过长,采用了 small queue 控制机制,缺省情况下,该机制允许本地队列仅排入 1ms 的报文,该值就是 pacing_rate / 1000。当然,也可以配置一个更小的值:

/* TCP Small Queues :* Control number of packets in qdisc/devices to two packets / or ~1 ms.* (These limits are doubled for retransmits)* This allows for :*  - better RTT estimation and ACK scheduling*  - faster recovery*  - high rates* Alas, some drivers / subsystems require a fair amount* of queued bytes to ensure line rate.* One example is wifi aggregation (802.11 AMPDU)*/
static bool tcp_small_queue_check(struct sock *sk, const struct sk_buff *skb,unsigned int factor)
{unsigned int limit;limit = max(2 * skb->truesize, sk->sk_pacing_rate >> sk->sk_pacing_shift);limit = min_t(u32, limit,sock_net(sk)->ipv4.sysctl_tcp_limit_output_bytes);limit <<= factor;if (refcount_read(&sk->sk_wmem_alloc) > limit) {/* Always send skb if rtx queue is empty.* No need to wait for TX completion to call us back,* after softirq/tasklet schedule.* This helps when TX completions are delayed too much.*/if (tcp_rtx_queue_empty(sk))return false;set_bit(TSQ_THROTTLED, &sk->sk_tsq_flags);/* It is possible TX completion already happened* before we set TSQ_THROTTLED, so we must* test again the condition.*/smp_mb__after_atomic();if (refcount_read(&sk->sk_wmem_alloc) > limit)return true;}return false;
}

注意函数注释最后一句,“呜呼,有些网络设备却需要更大量的排队数据以确保良好的吞吐”,这种 “大队列需求” 似乎与 small queue 相悖,于是 TSQ 提供了相应接口可以修改缺省配置:

/* We need a bit of data queued to build aggregates properly, so* instruct the TCP stack to allow more than a single ms of data* to be queued in the stack. The value is a bit-shift of 1* second, so 7 is ~8ms of queued data. Only affects local TCP* sockets.*/
sk_pacing_shift_update(skb->sk, 7);

说的就是 Wi-Fi 的情况,因为它涉及到帧聚合,就需要有足够多的报文来配合底层做聚合,这又是一个通过多层耦合来优化性能的例子,更详细的可参考 Adapting TCP Small Queues for IEEE 802.11 Networks。

现在,剩下的问题是如何根据 tsq 反推出 tcp_notsent_lowat 的配置以获得最佳吞吐延时比。先看 lowat 的取值:

static inline u32 tcp_notsent_lowat(const struct tcp_sock *tp)
{struct net *net = sock_net((struct sock *)tp);return tp->notsent_lowat ?: net->ipv4.sysctl_tcp_notsent_lowat;
}

显然取一个固定配置站在性能优化的视角是不合理的(但足够通用),它应该被实时计算出来,下面的函数会更好吗:

static inline u32 tcp_notsent_lowat(const struct tcp_sock *tp)
{struct sock *sk = (struct sock *)tp;  struct net *net = sock_net(sk);unsigned int limit;int inc_dec = ...; // 考虑到传输层和 IP 层计算(如拥塞控制算法的开销)损耗的时间,这个值...limit = max(2 * skb->truesize, sk->sk_pacing_rate >> sk->sk_pacing_shift);limit = min_t(u32, limit,sock_net(sk)->ipv4.sysctl_tcp_notsent_lowat);limit += inc_dec;return limit; 
}

这意味着在始发站构建了两道闸门构成的两座小坝。获得最佳吞吐延时比的途径就是维持这两座小坝处于低水位,快空了补,越界就截止,或许可以设置 low,high 水位来更平滑控制,但我知道,这或许就是 mptcp meta_sk 和 subflow 队列控制算法的答案。

现在到了形而上的时间,引自昨天发的朋友圈。

昨天写了一篇关于 mptcp 的随笔,格调依然如故,我不是说关于 CPU,内存,锁相关的主机优化技术没用,也从没有觉得 DPDK,XDP,eBPF 等通用技术没用(我自己在这些方面也是老手),我只是更关注网络性能本身,和 CPU 相比,即使数据中心网络传输也要慢至少一个数量级,真正的网络技术和主机根本就不在一个频道工作,这是两个领域,更何况即使是 DCN,我也从没把它当做网络看,只是看做主机总线的延伸。举个例子,即使我坐最快的飞机去里斯本,飞行的时间也比我在客厅卧室卫生之间兜兜转转慢几个数量级,我会更关注我在路上干什么,而不是纠结是先拉屎还是先收拾行李。

所以我更 concern 的是分布式一致性,博弈均衡,拥塞控制算法,流量调度策略,流量工程,社会工程学这些东西,这也正是我写的东西虽论技术,却根本不像技术文档的原因,如果不是你的菜,你可能根本不知道我的文章在说什么,完全学不到具体怎么做的技术,我也不解释,但如果是你的菜,看一眼就会觉得全是宝藏…

浙江温州皮鞋湿,下雨进水不会胖。

http://www.xdnf.cn/news/47773.html

相关文章:

  • 专题讨论:BST树上的添加与删除
  • mysql的5.7版本与8.0版本的差异与兼容性
  • Flink介绍——实时计算核心论文之MillWheel论文详解
  • 计算机视觉7——齐次坐标与相机内外参
  • transformer注意力机制
  • 集合框架拓展--stream流的使用
  • 理解 React 的 useEffect
  • 代理模式(Proxy Pattern)
  • 返回内容协商,@ResponseBody 注解
  • C++面试题集合(附答案)
  • [Windows]_[VS2017]_[如何进行远程调试程序]
  • 计算机视觉与深度学习 | 工业视觉缺陷检测如何检小缺陷?背景概述,原理,检测难点,常用的检测算法,算法评估指标,新项目算法选择,算法部署
  • 【Oracle专栏】Oracle中的虚拟列
  • Linux文件时间戳详解:Access、Modify、Change时间的区别与作用
  • PCA——主成分分析数学原理及代码
  • 小迪抓包技术算法加密(6-9天)
  • Dify部署内网时遇到的代理问题及解决办法
  • 【Python爬虫详解】第一篇:Python爬虫入门指南
  • B+树节点与插入操作
  • git清理--解决.git文件过大问题
  • 基于蒙特卡洛模拟与时间序列分析的美的集团财务预测模型研究
  • DeepSeek 助力 Vue 开发:打造丝滑的二维码生成(QR Code)
  • 常用的验证验证 onnxruntime-gpu安装的命令
  • WIN10重启开机不用登录,直接进入桌面
  • Java【网络原理】(4)HTTP协议
  • Redis 的几种数据类型
  • 【Linux我做主】GDB调试工具完全指南
  • Pandas数据可视化
  • UE5 UI 教程系列全集
  • 从入门到精通汇编语言 第六章(中断及外部设备操作)