周五了解到一个叫做Flowlet的东西,比较有意思。大体上说来,它是由一个问题而引出的一个解决方案,由于理解还不够深入,所以也暂时只能这么说。

  我先从问题说起。

ISP的动态负载均衡

由于公共骨干网上流量的不确定性,每一条链路的负载不尽相同,为了保证总带宽的最佳利用率,ISP往往会做一些动态负载均衡的策略。如下图所示:

*
时刻1:


*
时刻2:


packet负载均衡和flow负载均衡

到底是按照packet做负载均衡呢还是按照一个五元组flow来做负载均衡呢?这是一个问题,很多人在做负载均衡的时候都会面临这个选择问题。


  具体来讲,如果一条flow是强序的,那么基于packet的负载均衡将会导致乱序,这是设备在做负载均衡时要避免的,比如TCP就不能基于packet来做负载均衡,而对于UDP这种协议便可以。

  目前从主机到中间设备,几乎所有的从板卡,网卡队列,到CPU中断,到hash算法,均有机制保证TCP的强序性。

TCP的问题

正是由于TCP是强序的,所以TCP便无法基于packet做负载均衡,也就意味着,只要一条TCP流已经发起了,它就几乎不能再改变底层链路了
,至少是最好不要改变底层链路,因为一旦底层链路改变,TCP将增加面临乱序的概率。

幸亏TCP是burst发送

前面我的描述其实隐含了一个假设,即TCP flow的packet是平滑发送的:



然而实际上,不管是实际抓包(你可以观测抓包的tcptrace图)还是从具体实现(你可以看30年内任何TCP实现的源码,比如cubic,vegas…
)上看,你会发现TCP packet的发送其实是burst的:




哈哈,可乘之机!看到两批次burst之间的时间间隙没有,在这种间隙足够大的时候切换下层链路是一个好的时机。这意味着旧链路上的packet均已经离开了链路或者至少将要离开链路,这个时候切换链路将不会造成乱序,不会破坏TCP的强序要求。

  嗯,这就是Flowlet。

Flowlet

一般而言,英文中的“-let”后缀代表“微小”的意思,比如booklet,houselet,以及Java
applet…一个Flowlet代表的就是一条小流。如下图所示:




在宏观的观感上,可以把一条flow看成是多个flowlet组成的,负载均衡现在就是基于flowlet来进行了,好吧,又引入了一个中间层,它既不是packet,也不是flow,而是大于packet小于flow的flowlet,哈哈,很有意思!

  那么到底如何定量的去切分flowlet呢?这里给出一个公式:

已知两条链路的延迟分别为D1,D2,设一个数α,当α满足下面的条件:已知两条链路的延迟分别为D1,D2,设一个数α,当α满足下面的条件:
α>|D1−D2|α>|D1−D2|
一条flow便可以通过α来切割为不同的flowlet一条flow便可以通过α来切割为不同的flowlet

αα建议50ms50ms应该不错了。

BBR之后一切将不再


我个人觉得flowlet的理念非常Q,perfect,通过一个新的层次解决了强序flow的负载均衡问题。然而它借用了一个TCP实现上的问题或者说是bug,即burst机制。所谓借着坏事干好事。


  在TCP的AIMD模型下,pacing发送几乎是不可能的,因为pacing的计算会破坏AIMD的盲决策,最终的控制模型将会畸变。然而近期Google的研究证明简单的AIMD用于TCP传输其实是错误的,而引入了一中新的BBR
pacing传输模型,这个BBR一下子把TCP拥塞控制算法引入了2.0时代!

  我想表达什么?

  在BBR pacing模型下,我们假设BBR已经完美升级到了它应该成为的样子,解决了一系列的失速,误判等问题,届时TCP
packet的发送将会是下面的样子:

你可能再也捕捉不到那个flowlet中的αα了,因为pacing rate的精确计算机制不会允许αα那么久的空窗期存在!


  但BBR最终至多只是终结了flowlet在TCP上的具体实现,它无法终结flowlet的理念。拥塞控制和负载均衡是两个不同的领域,虽然有所关联但却井水不犯河水,拥塞控制说的是,当发现拥塞,要怎么做,负载均衡说的是,它可以帮忙分担拥塞。

Linux RFS中的影子

上周写的那篇:
合并N个有序链表与FQ公平调度 <https://blog.csdn.net/dog250/article/details/80234049>:
https://blog.csdn.net/dog250/article/details/80234049
<https://blog.csdn.net/dog250/article/details/80234049>
我找到了一道面试题或者说作业题在Linux中的影子,在我第一次听闻flowlet的昨天,我想Linux RPS/RFS也有该理念的实现,具体看下面这段代码:
/* * If the desired CPU (where last recvmsg was done) is * different from
current CPU (one in the rx-queue flow * table entry), switch if one of the
following holds: * ... * - The current CPU's queue tail has advanced beyond the
* last packet that was enqueued using this table entry. * This guarantees that
all previous packets for the flow * have been dequeued, thus preserving in
order delivery. */ if (unlikely(tcpu != next_cpu) && (... ((int
)(per_cpu(softnet_data, tcpu).input_queue_head - rflow->last_qtail)) >=0)) {
tcpu = next_cpu; rflow = set_rps_cpu(dev, skb, rflow, next_cpu); }
后记

非常感激总是有人帮助我让我重新思考技术的本质!今天周六一觉睡到8点半,所以这篇文章到了10点多才分享出来,迟来了,但总比没有好。

  对flowlet理解不深,不到一日而已,如果有需要讨论的,或者说发现我的说法有错误的,希望能及时指出,我会及时回复。

  上午积累了很多的家务事,申请了延后执行,这快到中午了,估计也干不完了,疯子大人和小小马上也就回来了,等待接受批评,但能总结出这篇文章,也算欣慰了。

友情链接
KaDraw流程图
API参考文档
OK工具箱
云服务器优惠
阿里云优惠券
腾讯云优惠券
华为云优惠券
站点信息
问题反馈
邮箱:[email protected]
QQ群:637538335
关注微信