精华内容
下载资源
问答
  • bbr
    千次阅读
    2022-01-04 18:14:09

     Google bbr源码分析 - wa小怪兽 - 博客园    bbr源码的comments.包含流程图和steps
    webrtc中BBR算法源码分析_occupy8的专栏-CSDN博客
    BBR拥塞控制算法 - 知乎
    TCP Pacing功能_redwingz的博客-CSDN博客   pacer

    /* Bottleneck Bandwidth and RTT (BBR) congestion control
     *
     * BBR congestion control computes the sending rate based on the delivery
     * rate (throughput) estimated from ACKs. In a nutshell:
     *
     *   On each ACK, update our model of the network path:
     *      bottleneck_bandwidth = windowed_max(delivered / elapsed, 10 round trips)
     *      min_rtt = windowed_min(rtt, 10 seconds)
     *
    更多相关内容
  • wget -N --no-check-certificate && chmod +x bbr.sh && bash bbr.sh 检查BBR运行状态 bash /root/bbr.sh status 控制,没有卸载选项。 (安装|启动|停止|检查状态) bash /root/bbr.sh {install | start | stop | ...
  • 采用低温小梁弯曲试验(BBR)对6种不同种类的沥青低温性能进行评价,利用时间温度等效原理获得了沥青的低温劲度模量主曲线,通过Burgers模型方程对BBR蠕变劲度曲线及其劲度模量主曲线进行拟合.结果表明:沥青的劲度模量...
  • bbr

    2021-02-28 03:57:50
    bbr
  • 与Loss-Base的算法相比, BBR算法可以有效地控制网络延时, 最大限度避免网络排队的情况, 在丢包率较高的情况下仍可以保持一定的带宽利用率和较低的链路延时, 因此适合于DCCP实时流媒体的应用的协议. 本文在DCCP中...
  • 适用于ns-2的Google BBR实施 这是要在上使用的ns-2网络模拟器的Google BBR TCP拥塞控制算法的实现 这是一个未完成的实现,欢迎贡献。 TL; DR 该文件夹必须位于ns-allinone-2.35旁边(与ns-allinone-2.35处于同一...
  • bbr:安装bbr加速

    2021-02-16 21:38:03
    bbr 安装bbr加速
  • 来自Loc大佬Yankee魔改的BBR的Debian一键安装包 安装命令 wget -N --no-check-certificate https://raw.githubusercontent.com/FunctionClub/YankeeBBR/master/bbr.sh && bash bbr.sh install 然后根据提示重启系统...
  • 具体案例,手把手教怎么建立离散元模型。BBR流变试验的主要目的是测定沥青胶浆的弯曲蠕变劲度和m值。模拟试验的试件制备和伺服加载过程,并获取试验过程中的加载轴位移。
  • 基于BBR研究体系,对特厚煤层综放开采的放煤方式进行了优化研究,提出了分段大间隔放煤方式;给出了特厚顶煤条件下合理放煤间隔的确定公式,并讨论了其关于破碎顶煤厚度、破碎直接顶厚度和松动体偏心率的敏感性;最后采用...
  • BBR拥塞控制算法在无线网络中的性能改进.pdf
  • bbr2:Google BBRv2内核

    2021-04-26 05:40:59
    bbr2 Google BBRv2内核
  • OpenVZ 魔改 BBR - lkl-rinetd 一键脚本1.1.0版本开始使用要求以下:OpenVZ64 bitRam >> 64M更新: 2018-03-24 新增 多网卡 适配Debian or Ubuntu适用于 单网卡(单 IP) 服务器:wget ...多网卡(多 IP) 服务器,会为...
  • BBR算法在高速移动网络中的应用及改进,吴旭,徐明伟,传统基于丢包类型的拥塞控制算法在丢包率较大的高速移动网络中的传输性能会受到较大影响。谷歌提出的BBR算法不再将丢包视为拥塞,
  • BBR到底好在哪里?

    千次阅读 2021-08-20 09:36:04
    都说BBR好,特别是在长传场景,一试就知道,也有很多分析BBR细节的文章,但很少有在理论上详细对比BBR和传统Loss based CCA的,BBR到底好在哪里,周末例行写作,今天的主题就是它了。 先从抗丢包说起。BBR的抗丢包...

    BBR到底好在哪里?

    都说BBR好,特别是在长传场景,一试就知道,也有很多分析BBR细节的文章,但很少有在理论上详细对比BBR和传统Loss based CCA的,BBR到底好在哪里,周末例行写作,今天的主题就是它了。

    先从抗丢包说起。BBR的抗丢包能力很强,这从BBR paper里的一张图中可以看出来:
    在这里插入图片描述
    该图的注释如下:

    Figure 8 shows BBR vs. CUBIC goodput for 60-second flows on a 100-Mbps/100-ms link with 0.001 to 50 percent random loss. CUBIC’s throughput decreases by 10 times at 0.1 percent loss and totally stalls above 1 percent. The maximum possible throughput is the link rate times fraction delivered (= 1 - lossRate). BBR meets this limit up to a 5 percent loss and is close up to 15 percent.

    只要丢包率在20%以内,BBR的吞吐似乎不受丢包的影响,但这是为什么?下面简单推导一下BBR的理论最好表现。

    设丢包率为 p p p,BltBW为 b b b,很显然有效传输率为 1 − p 1-p 1p,BBR在ProbeBW状态的1.25倍速的up probe探测,若要抵消丢包,维持带宽 b b b,则必须保证:

    1.25 b ( 1 − p ) = b 1.25b(1-p)=b 1.25b(1p)=b

    解得 p p p的值为 0.2 0.2 0.2,也就是 20 % 20\% 20%,正中上图。如果我们把固定的增益系数 1.25 1.25 1.25抽象成一个可以调节的参数,那么会得到一个在该增益系数下的丢包率崖点:

    p = 1 g a i n p=\dfrac{1}{gain} p=gain1

    丢包率超过该值,有效带宽将会以 p − 1 g a i n p-\dfrac{1}{gain} pgain1的速度快速衰减。

    现代网络中,丢包率超过 20 % 20\% 20%的链路很少见,在大多数情况下,BBR都能用 1.25 1.25 1.25的增益抵抗丢包损失。如果事先知道丢包率确实很高,可以增大 g a i n gain gain值。

    那么CUBIC的那条曲线如何解释呢?

    CUBIC是Loss based CCA,由于CUBIC本质上是Reno的优化,为了避免在三次曲线上比划微积分,这里以Reno为例进行推导。

    Reno将丢包视为拥塞的信号,简化起见,设链路有且仅有一个queue buffer,其容量为 C C C,RTprop极短,因此可以忽略传播时延,RTT全部是排队时延。由于Reno的MD系数为 0.5 0.5 0.5,那么一个AIMD周期内,拥塞窗口 W W W将于 0.5 C 0.5C 0.5C开始执行AI过程,直到 W W W达到 C + 1 C+1 C+1,产生一个丢包。

    因此,可以认为丢包率就是在一个AIMD周期内传输总量的倒数,接下来求AIMD周期中的传输总量,即求解下图中阴影部分面积:
    在这里插入图片描述

    设传输总量为 N N N,则:

    N = ( 0.5 C ) 2 + 1 2 ( 0.5 C ) 2 = 0.375 C 2 N=(0.5C)^2+\dfrac{1}{2}(0.5C)^2=0.375C^2 N=0.5C)2+21(0.5C)2=0.375C2

    因此丢包率 p p p为:

    p = 1 0.375 C 2 + 1 ≈ 1 0.375 C 2 p=\dfrac{1}{0.375C^2+1}\approx \dfrac{1}{0.375C^2} p=0.375C2+110.375C21

    从而解得 C C C的值:

    C = 1 0.375 p C=\sqrt{\dfrac{1}{0.375p}} C=0.375p1

    这意味着丢包率 p p p与buffer容量 C C C有明确的一一对应关系。

    接下来求Reno的平均带宽。整个AIMD周期内一个RTT时间的平均发送量为:

    N r o u n d = 0.5 C + C 2 = 0.75 C N_{round}=\dfrac{0.5C+C}{2}=0.75C Nround=20.5C+C=0.75C

    那么带宽即为:

    T = 0.75 C R T T T=\dfrac{0.75C}{RTT} T=RTT0.75C

    将上述的 C C C带入,即:

    T = 0.75 R T T 1 0.375 p T=\dfrac{0.75}{RTT}\sqrt{\dfrac{1}{0.375p}} T=RTT0.750.375p1

    这就是Loss based CCA的response function,详细情况请看:
    https://blog.csdn.net/dog250/article/details/113874204

    有了有效带宽 T T T和丢包率 p p p的关系,在RTT固定的情况下,我们看下 T T T- p p p曲线的走势:
    在这里插入图片描述
    和本文最初的图是吻合的,丢包率会极大影响有效带宽。

    对于Loss based CCA的假设而言,丢包率唯一受queue buffer大小的影响,deep buffer场景下,buffer溢出前发送的数据总量很大,丢包率越低,如果遭遇shadow buffer,那么Loss based CCA和BBR相比,必跪。

    接下来看下deep buffer的情景。

    BBR在deep buffer场景下似乎并无优势,直观看就是,deep buffer场景下Loss based CCA的一个AIMD周期太久,这期间queue buffer将不断被挤压。

    BBR本身在和Loss based CCA coexist的时候,由于up probe增益无效,将会快速进入cwnd limited。关于该事件的建模分析,可以参考:
    https://www.cs.cmu.edu/~rware/assets/pdf/ware-imc2019.pdf

    设queue buffer大小为1,BltBW为 B B B,其中Loss based流排队数据大小为 p p p,那么BBR可用的空间就是 1 − p 1-p 1p。BBR的cwnd增益是2,cwnd的求法如下:

    c w n d = 2 × B W m a x × R T T m i n cwnd=2\times BW_{max}\times RTT_{min} cwnd=2×BWmax×RTTmin

    在上述场景下, B W m a x BW_{max} BWmax的值显然是:

    B W m a x = ( 1 − p ) B BW_{max}=(1-p)B BWmax=(1p)B

    而此时的 R T T m i n RTT_{min} RTTmin显然是已经存在在Loss based流排队数据的出队时间:

    R T T m i n = p B RTT_{min}=\dfrac{p}{B} RTTmin=Bp

    带入上面cwnd表达式:

    c w n d = 2 p ( 1 − p ) cwnd=2p(1-p) cwnd=2p(1p)

    把所有这些都画进一张图:
    在这里插入图片描述

    可以看到,cwnd limited期间,BBR的cwnd会被限制在 0 0 0 0.5 0.5 0.5个buffer之内,也就是说,BBR最多只能抢占一半的queue buffer,这便削弱了BBR和Loss based CCA coexist的时候抢占带宽的能力。

    或许你会觉得把cwnd gain从2提升到4。很显然,这将导致阴影部分的增加,BBR将会产生更多的丢包。更合理的曲线是下面的蓝色曲线,极大值更高以允许发送更多包,曲线整体左边倾斜以缩小BBR丢包区域面积:
    在这里插入图片描述
    若希望在BBR发送端得到蓝色曲线描述的cwnd表达式,则需要能够用现有的测量量凑出这个曲线表达式。这一点我还没有深入思考。

    似乎在deep buffer场景下,BBR全是劣势,然而这种劣势仅仅是理论上的,实验室结果。

    在真实的现网环境中,特别是大RTT的长传场景,全链路的buffer远远不止1个,且buffer是串联的,只要有一个buffer是shadow buffer,溢出时间就以它为准,表象上整个链路就是shadow buffer的,按照概率相乘的原理,全链路全部都是deep buffer的概率特别低。因此很难出现上述理论分析中的结果。

    除了buffer溢出丢包,还有很多噪声丢包,比如线路误码,信号衰减等等,Loss based CCA无法区分这些情况,它依然会假设buffer出现了溢出,进而执行MD。噪声丢包是随机的,时间越久丢包概率越大,因此RTT越大的场景,噪声丢包概率越大,其对Loss based流的影响也就越大。

    好了,总结一下。

    无论是全链路buffer串联,还是噪声随机丢包,Loss based CCA在deep buffer的优势都面临概率相乘的抵消,在空间上,所有的buffer全部是deep buffer的概率极低,在时间上,长时间不发生噪声随机丢包的概率极低。所有这些导致Loss based CCA未竟全功。

    以上就是BBR为什么好的原因。特别是在长传场景,BBR的优势是碾压的。

    我知道,肯定会有人抬杠称“我怎么试了下感觉BBR还没有CUBIC好…”诸如此类,在国内运营商环境,任何网络表现行为都可能出现,更何况BBR只是一个靠即时测量值试图自适应所有环境的CCA,怎么能指望它能cover住100%的场景呢?还是那句话,解决大部分问题就行了,至于那些点落零星,随他去吧。

    至于魔改,我是反对的,调下参数可以,但不建议修改机制,我之前经常魔改CCA,结果都是狗熊掰棒子,甚至捡了芝麻丢了西瓜,当你看到哪里的机制有瑕疵时,看看别的地方,搞不好别的地方已经解决了。当今这么多CCA,都是经过理论推演和实际测试过了的,理论上任何魔改的效果都是负向的。以下两种情况下,可以魔改:

    • 问题非常明确。
    • 为了完成KPI。

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

    展开全文
  • TCP bbr简介

    千次阅读 2020-12-14 17:06:31
    1、背景 现有的拥塞控制算法,如cubic,基于丢包检查,问题 1)、网络设备buffer大,导致bufferbloat只要不丢包,就会发送,这样就容易把网络...bbr_init,先将bbr->mode初始化成BBR_STARTUP; static void bbr

    1、背景

    现有的拥塞控制算法,如cubic,基于丢包检查,问题

    1)、网络设备buffer大,导致bufferbloat只要不丢包,就会发送,这样就容易把网络设备的buffer填充满,导致延时增加;

    2)、网络设备buffer小,容易丢包,拥塞算法根据丢包控制发包速率,导致整体带宽吞吐小;

    2、BBR四个阶段流程

    start up

    进入start up有两个时机:

    1)、初始化

    bbr_init,先将bbr->mode初始化成BBR_STARTUP;

    static void bbr_init(struct sock *sk)
    {
    	bbr_reset_startup_mode(sk);
    
    	cmpxchg(&sk->sk_pacing_status, SK_PACING_NONE, SK_PACING_NEEDED);
    }
    

    2)、tcp ack收到拥塞信号,如果当前处于probe rtt阶段,并且还没探测到最大带宽,则重新进入start up阶段;

    static void bbr_cwnd_event(struct sock *sk, enum tcp_ca_event event)
    {
    	struct tcp_sock *tp = tcp_sk(sk);
    	struct bbr *bbr = inet_csk_ca(sk);
    
    	if (event == CA_EVENT_TX_START && tp->app_limited) {
    		bbr->idle_restart = 1;
    		/* Avoid pointless buffer overflows: pace at est. bw if we don't
    		 * need more speed (we're restarting from idle and app-limited).
    		 */
    		//如果在probe bw阶段收到拥塞信号,则将pacing_gain调成成1,控制发包量
    		if (bbr->mode == BBR_PROBE_BW)
    			bbr_set_pacing_rate(sk, bbr_bw(sk), BBR_UNIT);
    		else if (bbr->mode == BBR_PROBE_RTT)
    			//如果是在probe rtt阶段收到拥塞信号,则判断当前如果还未探测到最大带宽,则重新
    			//进入start up阶段,否则进入probe bw阶段
    			bbr_check_probe_rtt_done(sk);
    	}
    }

    在start up阶段,bbr会以较大的pacing gain和cwnd gain发送数据包;

    static void bbr_update_gains(struct sock *sk)
    {
    	struct bbr *bbr = inet_csk_ca(sk);
    
    	switch (bbr->mode) {
    	case BBR_STARTUP:
    		bbr->pacing_gain = bbr_high_gain;  //2.95倍
    		bbr->cwnd_gain	 = bbr_high_gain;
    		break;
    }

    drain排空

    在start up阶段,以较大的pacing、cwnd速率发送消息包,因此探测到的bw也越来越大,当bbr发现已经探测到最大带宽时,此时链路上的设备buffer已经被填充满,开始进入drain阶段,将链路设备的buffer排空;

    static void bbr_check_drain(struct sock *sk, const struct rate_sample *rs)
    {
    	struct bbr *bbr = inet_csk_ca(sk);
    
    	if (bbr->mode == BBR_STARTUP && bbr_full_bw_reached(sk)) {
    		bbr->mode = BBR_DRAIN;	/* drain queue we created */
    		tcp_sk(sk)->snd_ssthresh =
    				bbr_inflight(sk, bbr_max_bw(sk), BBR_UNIT);
    	}	/* fall through to check if in-flight is already small: */
    	if (bbr->mode == BBR_DRAIN &&
    	    tcp_packets_in_flight(tcp_sk(sk)) <=
    	    bbr_inflight(sk, bbr_max_bw(sk), BBR_UNIT))
    		bbr_reset_probe_bw_mode(sk);  /* we estimate queue is drained */
    }

    在drain排空阶段,bbr会控制减少pacing的速率;

    static void bbr_update_gains(struct sock *sk)
    {
    	struct bbr *bbr = inet_csk_ca(sk);
    
    	switch (bbr->mode) {
    
    	case BBR_DRAIN: 
    		bbr->pacing_gain = bbr_drain_gain;	/* slow, to drain 1/2.9倍增益 */
    		bbr->cwnd_gain	 = bbr_high_gain;	/* keep cwnd */
    		break;
    }

    等到检测到in_flight的数据包个数少于BDP时,进入probe_bw阶段。

    static void bbr_check_drain(struct sock *sk, const struct rate_sample *rs)
    {
    	struct bbr *bbr = inet_csk_ca(sk);
    
    	if (bbr->mode == BBR_STARTUP && bbr_full_bw_reached(sk)) {
    		bbr->mode = BBR_DRAIN;	/* drain queue we created */
    		tcp_sk(sk)->snd_ssthresh =
    				bbr_inflight(sk, bbr_max_bw(sk), BBR_UNIT);
    	}	/* fall through to check if in-flight is already small: */
    	if (bbr->mode == BBR_DRAIN &&
    	    tcp_packets_in_flight(tcp_sk(sk)) <=
    	    bbr_inflight(sk, bbr_max_bw(sk), BBR_UNIT))
    		bbr_reset_probe_bw_mode(sk);  /* we estimate queue is drained */
    }
    

    Probe bw

    如何探测?

    进入probe_bw阶段,会使用以下的pacing增益系数不断循环探测;

    static const int bbr_pacing_gain[] = {
    	BBR_UNIT * 5 / 4,	/* probe for more available bw */
    	BBR_UNIT * 3 / 4,	/* drain queue and/or yield bw to other flows */
    	BBR_UNIT, BBR_UNIT, BBR_UNIT,	/* cruise at 1.0*bw to utilize pipe, */
    	BBR_UNIT, BBR_UNIT, BBR_UNIT	/* without creating excess queue... */
    };

    当处在某个phase下,判断当前满足条件,进入下一个phase;

    static bool bbr_is_next_cycle_phase(struct sock *sk,
    				    const struct rate_sample *rs)
    {
    	struct tcp_sock *tp = tcp_sk(sk);
    	struct bbr *bbr = inet_csk_ca(sk);
    	bool is_full_length =
    		tcp_stamp_us_delta(tp->delivered_mstamp, bbr->cycle_mstamp) >
    		bbr->min_rtt_us;
    	u32 inflight, bw;
    
    	/* The pacing_gain of 1.0 paces at the estimated bw to try to fully
    	 * use the pipe without increasing the queue.
    	 */
    	//如果是1倍增益,则探测时间到达一个min_rtt,就进入下一个phase
    	if (bbr->pacing_gain == BBR_UNIT)
    		return is_full_length;		/* just use wall clock time */
    
    	inflight = rs->prior_in_flight;  /* what was in-flight before ACK? */
    	bw = bbr_max_bw(sk);
    
    	/* A pacing_gain > 1.0 probes for bw by trying to raise inflight to at
    	 * least pacing_gain*BDP; this may take more than min_rtt if min_rtt is
    	 * small (e.g. on a LAN). We do not persist if packets are lost, since
    	 * a path with small buffers may not hold that much.
    	 */
    	//如果是大于1倍增益,则探测时间超过1个min_rtt并且有丢包或inflight达到该增益倍数下计算的bdp值
    	//则进入下一个phase
    	//正增益下,意图是提高链路的使用率
    	if (bbr->pacing_gain > BBR_UNIT)
    		return is_full_length &&
    			(rs->losses ||  /* perhaps pacing_gain*BDP won't fit */
    			 inflight >= bbr_inflight(sk, bw, bbr->pacing_gain));
    
    	/* A pacing_gain < 1.0 tries to drain extra queue we added if bw
    	 * probing didn't find more bw. If inflight falls to match BDP then we
    	 * estimate queue is drained; persisting would underutilize the pipe.
    	 */
    	//如果是小于1倍增益,则探测时间超过1个min_rtt并且infight达到1倍增益下的bdp值,则进入下一个phase
    	//减增益下,意图是释放链路上的带宽资源
    	return is_full_length ||
    		inflight <= bbr_inflight(sk, bw, BBR_UNIT);
    }
    

    探测何时结束?

    在probe_bw模式下,经过一轮的phase定义的不同pacing增益发包逻辑,探测到链路开始出现丢包,并且丢包率达到预期值后,认为开始探测到最大bw了,如果两次探测的最大bw在固定误差范围内,则设置本轮探测的bw为二者的平均值,然后进入lt_use_bw状态;

    static void bbr_lt_bw_interval_done(struct sock *sk, u32 bw)
    {
    	struct bbr *bbr = inet_csk_ca(sk);
    	u32 diff;
    
    	//本次计算得到的bw跟之前探测到的bbr->lt_bw误差在1/8内,则设置本轮探测的bw为二者的平均值
    	//然后设置lt_ues_bw为1,进入long term状态,暂时退出probe_bw,等待下一轮的探测周期
    	//在long term模式下,pacing_gain设置为1倍增益
    	if (bbr->lt_bw) {  /* do we have bw from a previous interval? */
    		/* Is new bw close to the lt_bw from the previous interval? */
    		diff = abs(bw - bbr->lt_bw);
    		if ((diff * BBR_UNIT <= bbr_lt_bw_ratio * bbr->lt_bw) ||
    		    (bbr_rate_bytes_per_sec(sk, diff, BBR_UNIT) <=
    		     bbr_lt_bw_diff)) {
    			/* All criteria are met; estimate we're policed. */
    			bbr->lt_bw = (bw + bbr->lt_bw) >> 1;  /* avg 2 intvls */
    			bbr->lt_use_bw = 1;
    			bbr->pacing_gain = BBR_UNIT;  /* try to avoid drops */
    			bbr->lt_rtt_cnt = 0;
    			return;
    		}
    	}
    	bbr->lt_bw = bw;
    	bbr_reset_lt_bw_sampling_interval(sk);
    }
    

    进入lt_use_bw状态后,当经过固定周期(bbr_lt_bw_max_rtts),probe_bw模式重新生效;

    static void bbr_lt_bw_sampling(struct sock *sk, const struct rate_sample *rs)
    {
    	struct tcp_sock *tp = tcp_sk(sk);
    	struct bbr *bbr = inet_csk_ca(sk);
    	u32 lost, delivered;
    	u64 bw;
    	u32 t;
    
    	//如果是在lt_use_bw状态下,判断当前处于probe_bw阶段,则经过bbr_lt_bw_max_rtts次有正常ack后
    	//重新进入prote_bw阶段
    	//当bbr->mode==BBR_RROBE_BW时,并不一定真的有去探测bw,而是要看当前是否处于lt_use_bw,
    	//每次probe_bw周期,都会重新探测到一个稳定的最大带宽,然后设置lt_use_bw为1
    	if (bbr->lt_use_bw) {	/* already using long-term rate, lt_bw? */
    		if (bbr->mode == BBR_PROBE_BW && bbr->round_start &&
    		    ++bbr->lt_rtt_cnt >= bbr_lt_bw_max_rtts) {
    			bbr_reset_lt_bw_sampling(sk);    /* stop using lt_bw */
    			bbr_reset_probe_bw_mode(sk);  /* restart gain cycling */
    		}
    		return;
    	}
    
    	/* Wait for the first loss before sampling, to let the policer exhaust
    	 * its tokens and estimate the steady-state rate allowed by the policer.
    	 * Starting samples earlier includes bursts that over-estimate the bw.
    	 */
    	if (!bbr->lt_is_sampling) {
    		if (!rs->losses)
    			return;
    		bbr_reset_lt_bw_sampling_interval(sk);
    		bbr->lt_is_sampling = true;
    	}
    
    	//探测probe_bw阶段,如果处于app_limited,则不进行探测
    	/* To avoid underestimates, reset sampling if we run out of data. */
    	if (rs->is_app_limited) {
    		bbr_reset_lt_bw_sampling(sk);
    		return;
    	}
    
    	if (bbr->round_start)
    		bbr->lt_rtt_cnt++;	/* count round trips in this interval */
    	if (bbr->lt_rtt_cnt < bbr_lt_intvl_min_rtts)
    		return;		/* sampling interval needs to be longer */
    	if (bbr->lt_rtt_cnt > 4 * bbr_lt_intvl_min_rtts) {
    		bbr_reset_lt_bw_sampling(sk);  /* interval is too long */
    		return;
    	}
    
    	/* End sampling interval when a packet is lost, so we estimate the
    	 * policer tokens were exhausted. Stopping the sampling before the
    	 * tokens are exhausted under-estimates the policed rate.
    	 */
    	//探测发送到有出现丢包,才会计算bw
    	if (!rs->losses)
    		return;
    
    	/* Calculate packets lost and delivered in sampling interval. */
    	lost = tp->lost - bbr->lt_last_lost;
    	delivered = tp->delivered - bbr->lt_last_delivered;
    	/* Is loss rate (lost/delivered) >= lt_loss_thresh? If not, wait. */
    
    	//丢包率至少要达到20%
    	if (!delivered || (lost << BBR_SCALE) < bbr_lt_loss_thresh * delivered)
    		return;
    
    	/* Find average delivery rate in this sampling interval. */
    	t = div_u64(tp->delivered_mstamp, USEC_PER_MSEC) - bbr->lt_last_stamp;
    	if ((s32)t < 1)
    		return;		/* interval is less than one ms, so wait */
    	/* Check if can multiply without overflow */
    	if (t >= ~0U / USEC_PER_MSEC) {
    		bbr_reset_lt_bw_sampling(sk);  /* interval too long; reset */
    		return;
    	}
    	t *= USEC_PER_MSEC;
    	bw = (u64)delivered * BW_UNIT;
    	do_div(bw, t);
    	bbr_lt_bw_interval_done(sk, bw);
    }
    

    Delivered速率怎么计算?

    如上图,t1时刻发送序列号seq=2,t2时刻发送3号skb,t3时刻发送4号skb,然后t4时刻ack4号skb;bbr在计算带宽时的原理就是计算某个skb从发送到被ack时一共delivered了多少个数据以及用了的时间interval_us,然后两者相除就得到当前的带宽值;

    由于存在延时ack等因素,因此可能计算出来的带宽值会偏差,bbr在计算interval_us时会同时计算发送时间及ack时间,然后取两者的小值,因此bw的计算方法如下:

    delivered = tp->delivered – tp->tp_pri_delivered

    Interverl_ns = min(send_usack_us)

    Bw = delivered / Interverl_ns

    Probe rtt

    每隔10s,bbr都会进行一轮min_rtt探测,探测周期为200ms,在probe_rtt阶段,bbr会降低发包速率,保证链路不会出现拥堵;

    static void bbr_update_gains(struct sock *sk)
    {
    	struct bbr *bbr = inet_csk_ca(sk);
    
    	switch (bbr->mode) {
    	case BBR_PROBE_RTT:
    		bbr->pacing_gain = BBR_UNIT;
    		bbr->cwnd_gain	 = BBR_UNIT;
    		break;
    	default:
    		WARN_ONCE(1, "BBR bad mode: %u\n", bbr->mode);
    		break;
    	}
    }
    
    static void bbr_update_min_rtt(struct sock *sk, const struct rate_sample *rs)
    {
    	struct tcp_sock *tp = tcp_sk(sk);
    	struct bbr *bbr = inet_csk_ca(sk);
    	bool filter_expired;
    
    	/* Track min RTT seen in the min_rtt_win_sec filter window: */
    	//每经过10s(bbr_min_rtt_win_sec),就会探测一次min_rtt
    	filter_expired = after(tcp_jiffies32,
    			       bbr->min_rtt_stamp + bbr_min_rtt_win_sec * HZ);
    	//10s超时后,判断本轮探测的rtt是否比之前探测的min_rtt更小,如果是,则更新min_rtt为本轮探测的rtt
    	if (rs->rtt_us >= 0 &&
    	    (rs->rtt_us <= bbr->min_rtt_us ||
    	     (filter_expired && !rs->is_ack_delayed))) {
    		bbr->min_rtt_us = rs->rtt_us;
    		bbr->min_rtt_stamp = tcp_jiffies32;
    	}
    
    	//检测周期到期,当前不是probe_rtt模式,则进入probe_rtt模式
    	if (bbr_probe_rtt_mode_ms > 0 && filter_expired &&
    	    !bbr->idle_restart && bbr->mode != BBR_PROBE_RTT) {
    		bbr->mode = BBR_PROBE_RTT;  /* dip, drain queue */
    		bbr_save_cwnd(sk);  /* note cwnd so we can restore it */
    		bbr->probe_rtt_done_stamp = 0;
    	}
    
    	if (bbr->mode == BBR_PROBE_RTT) {
    		/* Ignore low rate samples during this mode. */
    		tp->app_limited =
    			(tp->delivered + tcp_packets_in_flight(tp)) ? : 1;
    		/* Maintain min packets in flight for max(200 ms, 1 round). */
    		//设置本轮的采样周期(200ms),进入probe_rtt阶段后,会降低发包速率,等in_flight数据包
    		//个数降到bbr_cwnd_min_target开始探测rtt
    		if (!bbr->probe_rtt_done_stamp &&
    		    tcp_packets_in_flight(tp) <= bbr_cwnd_min_target) {
    			bbr->probe_rtt_done_stamp = tcp_jiffies32 +
    				msecs_to_jiffies(bbr_probe_rtt_mode_ms);
    			bbr->probe_rtt_round_done = 0;
    			bbr->next_rtt_delivered = tp->delivered;
    		} else if (bbr->probe_rtt_done_stamp) {
    			if (bbr->round_start)
    				bbr->probe_rtt_round_done = 1;
    			if (bbr->probe_rtt_round_done)
    				bbr_check_probe_rtt_done(sk);
    		}
    	}
    	/* Restart after idle ends only once we process a new S/ACK for data */
    	if (rs->delivered > 0)
    		bbr->idle_restart = 0;
    }
    

    当一轮rtt探测结束后,通过bbr_reset_mode重新进入probe_bw或start up阶段;

    static void bbr_check_probe_rtt_done(struct sock *sk)
    {
    	struct tcp_sock *tp = tcp_sk(sk);
    	struct bbr *bbr = inet_csk_ca(sk);
    
    	if (!(bbr->probe_rtt_done_stamp &&
    	      after(tcp_jiffies32, bbr->probe_rtt_done_stamp)))
    		return;
    
    	bbr->min_rtt_stamp = tcp_jiffies32;  /* wait a while until PROBE_RTT */
    	tp->snd_cwnd = max(tp->snd_cwnd, bbr->prior_cwnd);
    	bbr_reset_mode(sk);
    }
    

    BBR的输出

    数据包ack后进入bbr模块处理,处理完成后,bbr模块会有两个输出,一个是根据bw设置的pacing速率,一个是根据bdp计算得到的cwnd;

    Pacing速率

    Pacing处理

    pacing速率的计算:

    bbr的pacing处理有两种方式:

    1)、依赖于tc-fq的pacing

    当使用tc-fq时,qdisc默认会使能rate_enable限速,这个流程也会利用bbr算法计算得到的sk_pacing_rate完成pacing功能;

    static struct sk_buff *fq_dequeue(struct Qdisc *sch)
    {
    	//rate_enable模式使能
    	if (!q->rate_enable)
    		goto out;
    
    	/* Do not pace locally generated ack packets */
    	if (skb_is_tcp_pure_ack(skb))
    		goto out;
    
    	rate = q->flow_max_rate;
    	if (skb->sk)
    		rate = min(skb->sk->sk_pacing_rate, rate);
    
    	if (rate <= q->low_rate_threshold) {
    		f->credit = 0;
    		plen = qdisc_pkt_len(skb);
    	} else {
    		plen = max(qdisc_pkt_len(skb), q->quantum);
    		if (f->credit > 0)
    			goto out;
    	}
    	if (rate != ~0U) {
    		u64 len = (u64)plen * NSEC_PER_SEC;
    
    		if (likely(rate))
    			do_div(len, rate);
    		/* Since socket rate can change later,
    		 * clamp the delay to 1 second.
    		 * Really, providers of too big packets should be fixed !
    		 */
    		if (unlikely(len > NSEC_PER_SEC)) {
    			len = NSEC_PER_SEC;
    			q->stat_pkts_too_long++;
    		}
    		/* Account for schedule/timers drifts.
    		 * f->time_next_packet was set when prior packet was sent,
    		 * and current time (@now) can be too late by tens of us.
    		 */
    		if (f->time_next_packet)
    			len -= min(len/2, now - f->time_next_packet);
    		f->time_next_packet = now + len;
    	}
    out:
    	qdisc_bstats_update(sch, skb);
    	return skb;
    }
    

    2)、tcp主动pacing

    bbr_init时,默认使能SK_PACING_NEEDED;

    static void bbr_init(struct sock *sk)
    {
    	cmpxchg(&sk->sk_pacing_status, SK_PACING_NONE, SK_PACING_NEEDED);
    }
    

    tcp_write_xmit的时候,通过tcp_pacing_check判断当前是否已经启动pacing高精度定时器,如果已经启动,则退出xmit流程;

    static bool tcp_write_xmit(struct sock *sk, unsigned int mss_now, int nonagle,
    			   int push_one, gfp_t gfp)
    {
    	struct tcp_sock *tp = tcp_sk(sk);
    	struct sk_buff *skb;
    	unsigned int tso_segs, sent_pkts;
    	int cwnd_quota;
    	int result;
    	bool is_cwnd_limited = false, is_rwnd_limited = false;
    	u32 max_segs;
    
    	sent_pkts = 0;
    
    	tcp_mstamp_refresh(tp);
    	if (!push_one) {
    		/* Do MTU probing. */
    		result = tcp_mtu_probe(sk);
    		if (!result) {
    			return false;
    		} else if (result > 0) {
    			sent_pkts = 1;
    		}
    	}
    
    	max_segs = tcp_tso_segs(sk, mss_now);
    	while ((skb = tcp_send_head(sk))) {
    		unsigned int limit;
    
    		if (tcp_pacing_check(sk))
    			break;
        ...
    }

    __tcp_transmit_skb判断是否需要tcp层做pacing,需要的话就启动高精度定时器;

    static int __tcp_transmit_skb(struct sock *sk, struct sk_buff *skb,
    			      int clone_it, gfp_t gfp_mask, u32 rcv_nxt)
    {
    	...
    	if (skb->len != tcp_header_size) {
    		tcp_event_data_sent(tp, sk);
    		tp->data_segs_out += tcp_skb_pcount(skb);
    		tp->bytes_sent += skb->len - tcp_header_size;
    		tcp_internal_pacing(sk, skb);
    	}
    	...
    }
    static void tcp_internal_pacing(struct sock *sk, const struct sk_buff *skb)
    {
    	u64 len_ns;
    	u32 rate;
    
    	if (!tcp_needs_internal_pacing(sk))
    		return;
    	rate = sk->sk_pacing_rate;
    	if (!rate || rate == ~0U)
    		return;
    
    	/* Should account for header sizes as sch_fq does,
    	 * but lets make things simple.
    	 */
    	//sk_pacing_rate表示1分钟能发送的字节数
    	//skb->len / rate表示发送skb->len字节数需要的时间长度(长度是分钟)
    	//len_ns = skb->len / rate * NSEC_PER_SEC即将时间换算成纳秒,然后启动pacing高精度定时器
    	len_ns = (u64)skb->len * NSEC_PER_SEC;
    	do_div(len_ns, rate);
    	hrtimer_start(&tcp_sk(sk)->pacing_timer,
    		      ktime_add_ns(ktime_get(), len_ns),
    		      HRTIMER_MODE_ABS_PINNED_SOFT);
    	sock_hold(sk);
    }
    

    拥塞窗口cwnd

    static void bbr_set_cwnd(struct sock *sk, const struct rate_sample *rs,
    			 u32 acked, u32 bw, int gain)
    {
    	struct tcp_sock *tp = tcp_sk(sk);
    	struct bbr *bbr = inet_csk_ca(sk);
    	u32 cwnd = tp->snd_cwnd, target_cwnd = 0;
    
    	if (!acked)
    		goto done;  /* no packet fully ACKed; just apply caps */
    
    	//第一次进入recovery状态时,返回true,此时cwnd=tcp_packets_in_flight(tp) + acked,因此这时候主要还是
    	//考虑链路上的数据包守恒,ack多少个数据包就发送多少个数据包,相当于保持cwnd不变
    	//退出recovery或loss状态时,cwnd=进入recovery或loss状态时的cwnd
    	if (bbr_set_cwnd_to_recover_or_restore(sk, rs, acked, &cwnd))
    		goto done;
    
    	/* If we're below target cwnd, slow start cwnd toward target cwnd. */
    	// 1、先根据bdp计算得到target_cwnd
    	target_cwnd = bbr_bdp(sk, bw, gain);
    	target_cwnd = bbr_quantization_budget(sk, target_cwnd);
    	//当cwnd=cwnd+acked时表示: 本次ack p个数据包,则可发送2*p个数据包
    	//本次ack p个包后,in_flight数据包少了p个,所以本来cwnd里就有空闲的p个数据包可以发送,再加acked
    	//那就相当于可以发送2*p个数据包了
    	if (bbr_full_bw_reached(sk))  /* only cut cwnd if we filled the pipe */
    		cwnd = min(cwnd + acked, target_cwnd);
    	else if (cwnd < target_cwnd || tp->delivered < TCP_INIT_CWND)
    		cwnd = cwnd + acked;
    	cwnd = max(cwnd, bbr_cwnd_min_target);
    
    done:
    	tp->snd_cwnd = min(cwnd, tp->snd_cwnd_clamp);	/* apply global cap */
    	if (bbr->mode == BBR_PROBE_RTT)  /* drain queue, refresh min_rtt */
    		tp->snd_cwnd = min(tp->snd_cwnd, bbr_cwnd_min_target);
    }
    

     

    展开全文
  • 路由器bbr的web实现

    2019-03-29 01:26:18
    NULL 博文链接:https://njuptlinjian.iteye.com/blog/1291325
  • 流媒体学习之路(BBR算法应用)——BBR算法简介 文章目录流媒体学习之路(BBR算法应用)——BBR算法简介

    流媒体学习之路(BBR算法应用)——BBR算法简介


      前段时间进行弱网优化相关的工作,因此对BBR算法进行了较为详细的调研在此记录一下。

    一、弱网优化简介

      目前用户的网络环境相比过去十年已经有了较大的改善。但是针对移动网络以及其他弱网环境,我们仍需想办法让我们的服务做到处变不惊。也就是在任何环境下,都能提供流畅的画面播放(针对音视频传输方面)。
      目前常使用的是RTP协议来进行音视频的传输,而这个协议是基于UDP的,因此引入了很多保证网络传输质量的模块。针对音视频传输常用的拥塞控制算法有:webrtc中的transport-cc、quic中的bbr等,这两种算法都是基于延迟进行带宽估计的。而媒体数据传输对实时性有极高的要求,同时需要保证数据的连续性。因此不仅要关注传输过程中丢包的情况,还需要应对延迟增大的问题。

      弱网优化这个命题非常大,因为应对弱网的方法是多样的。例如:补包策略(ACK、NACK等)、前向纠错(FEC)、码率自适应等。补包与前向纠错都有各自的优缺点,补包策略是最简单有效的抗弱网方式。
    在这里插入图片描述

    1.1 补包

      简单来说就是当网络传输时发送丢包,我们立即通知对方进行补发的一种机制。这种方式将会消耗较多的带宽资源。

    1.2 前向纠错

      前向纠错的方式则是通过提前封装冗余包来抵抗网络的丢包问题,当对端接收时发生丢包,那么可以根据冗余包的数据进行恢复,实现抗弱网。

    1.3 自适应

      自适应方法的关键就是对带宽的预估以及对输出码率的调整,除了要对网络状况有一个准确的估计之外,还需要找到一个合适的码率输出值。我们需要进行大量的实验来保证用户体验,需要考虑是牺牲帧率还是牺牲分辨率来降低码率。

      事实上,所有抗弱网实践中,我们都不可能依靠某一个方法达到最佳效果,因此大部分厂商在实现时所有场景都会用到。下面,我们聚焦在带宽估计的部分,来分析一下quic中的BBR算法在RTC中的应用。

    二、BBR算法

      自1980年代,TCP的拥塞控制算法现世到如今,网络环境发生了巨大的变化。原先基于数据损耗的拥塞算法已经无法满足当前的传输控制需求。因此谷歌提出了一种基于带宽瓶颈计算数据往返时间来实现的拥塞控制算法(BBR——Bottleneck Bandwidth and RTT)。该算法通过安全而又直接的带宽检测迅速获得当前链路的带宽值、rtt。同时,一改旧拥塞算法的损耗参考思路,在拥塞发生时BBR算法依然可以插手cwnd的调整,有效解决了硬着陆的问题。

    2.1 算法组成

      BBR算法的组成其实非常简单,主要由以下五个部分组成:
      1.即时速率的计算
      带宽计算是整个BBR算法的基准,其中即时带宽统计的方法是具有里程碑意义的。它突破旧算法的信令参考,不再关心数据内涵。化繁为简转而关注数据量本身,无论是SACK、RACK、RTO都不再是其关注的重点,使得整个检测流程变得更直接更高效。
      2.RTT的跟踪
      RTT与上述的带宽可以有效的描述当前链路的最大容量。BBR算法探测的是RTT的最小值,它无时无刻都在尝试达到这个链路的最大容量,因此展现出了极高的带宽利用率。
      3.BBR pipe状态机的维持
      BBR算法根据互联网的拥塞行为有针对性地定义了4中状态,即STARTUP,DRAIN,PROBE_BW,PROBE_RTT。BBR通过对上述计算的即时带宽bw以及rtt的持续观察,在这4个状态之间自由切换。
      4.结果输出-pacing rate和cwnd
      pacing rate可以控制数据包的发送间隔,在每一时刻都会有一个与之匹配的pacing rate来控制发送,大大提高了整个容量的利用。
    与传统意义的拥塞算法相比,cwnd不再是BBR算法的唯一输出。事实上,更重要的是BBR定义了pacing rate这个参考值。cwnd只能控制发出去多少数据,但无法控制怎么去发送这些数据。因此整个容量存在许多不充分利用的情况甚至会造成路由器排队形成深队列。
      5.其它外部机制的利用 fq,rack等
      BBR算法对外部机制进行了高效的利用。例如:使用了FQ中的平缓发送来实现pacing rate等。

    2.2 状态机

      以上的五个内容通过BBR内部的四个状态切换来实现动态控制。下图引用网络中对bbr状态机常用的描述图:
    在这里插入图片描述

      阶段一(STARTUP),使用最高的增益值来进行发送增长,当计算到带宽不在增加时则进入下一阶段;
      阶段二(DRAIN),前面的发送速度增长非常快,因此很容易在带宽最高的那一段时间内造成管道超载,因此需要进行一部分的排空操作;
      阶段三(PROBE_BW),该状态为稳定状态。当管道排空完毕,带宽正好满足数据传输需求,因此进入了一个传输大小稳定的一个时期;
      阶段四(PROBE_RTT),当网络出现拥塞时将会进入该状态,判定条件为:RTT时间超过probe窗口而且当前bw已满或者在rtt更新周期内没有发现更小的rtt。这个状态将会减少发送数量,达到排空的目的。

    2.3 BBR优劣分析

      我们刚刚讨论的都是bbr的第一个版本,近期bbr已经更新了它的v2版本,但是这里还是先介绍v1版本中的缺陷。

      在BBRv1版本的实践中发现,该算法存在以下几个缺陷(TCP应用相关):
      1.过于激进的启动操作:
    由于启动阶段探测的容积较大,导致发送速率以及数量偏高。这会影响正在传输的TCP流,形成深队列,造成链路中传输拥塞。[1]
      2.过于激进的带宽探测:
    当整个链路存在多个BBRv1算法的TCP流时,BBR会高估整个链路的传输容量,导致每个TCP流形成竞争造成大量丢包。[2]
      3.不公平的RTT探测:
    当一个链路中存在两个BBRv1的流,其中一个流的RTT大于另一个流的RTT时。计算出来的BDP相差较大,RTT较大的流会占用更多的带宽,形成整个传输的不公平性。[2]
      4.损耗型算法的不公平竞争:
    此处举例一个比较好理解的例子:例如当传统基于损耗的拥塞算法与BBRv1同时共享一个窄带通道。当发生拥塞时,虽然BBRv1检测到了带宽下降但是无法检测到拥塞产生。相反传统的拥塞算法会检测丢包并强行降低发包,这导致了BBRv1算法的非法竞争。

      针对上面的缺陷,BBRv2算法于 IETF-102 和 IETF-104进行了改进:
      1.缓解了Startup阶段以及激进发送带来的丢包和时延;
      2.改善了与传统CC算法并存时的公平性问题;
      3.重构了PROBE_RTT这个BBR状态的实现。

    2.3.1 PROBE_BW重构

      PROBE_BW变化体现在三方面(引用dog250大佬的解释:https://blog.csdn.net/dog250/article/details/81141638):
      Cruise:不再像BBR v1.0那般保持estimateBDP*1gain这样,而是确保inflight在inflight_hi之下保留一定的空间。
    在这里插入图片描述

      ProbeMore:采用了类似SlowStart或者Startup阶段的指数级增加探测包的策略,同时在丢包时设置inflight_hi…
    在这里插入图片描述

      ProbeDrain:由于BBR v1.0收敛实在太慢,BBR v2.0采用了一次性收敛的策略。

    2.3.2 优势

    抗丢包性能

      其实稍微了解bbr的朋友可能都知道,bbr算法只是计算即时带宽并不会对丢包进行参考,那么何来的抗丢包性能呢?这里的抗丢包性能可以这么理解。例如:当网络中存在5%的丢包时,大部分的数据往返时间都是确定的,那么总能计算出一个复合算法要求的最小rtt,那么整体的rtt不会发送太大的变化,因此估计出来的带宽不会有太大的影响。当网络中存在较大的丢包(>5%)时,那么我们计算的rtt将会由于中间丢包造成影响,因此带宽会出现所谓的断崖式下跌的情况:
    在这里插入图片描述

    低延迟与高抢占

      我们可知在所有的TCP拥塞算法中,BBR都具备极高的抢占能力,这是因为其启动阶段的超高增益导致的。BBR算法的思想是以最短的时间去探测到带宽的最大值,以便时刻维持在PROBE_BW这样的状态下。而且,即使在稳定阶段,BBR仍然会根据周期变化来上调我们的发送增益,不断试探是否存在更多的带宽。这种方式带来的优势是:数据包可以尽可能的发送到网络中,保证了低延迟以及高抢占。

    准确的带宽反馈

      BBR算法使用的是及时带宽,也就是说BBR永远不去做估计,只是反应当前的最大带宽情况。这样的方式比普通的带宽估计更准确,更可靠。

    三、应用

      经过上述的介绍,我们基本了解了BBR的算法基础以及优劣,下面我介绍一些对其在RTC中应用的尝试。

    3.1 RTC场景

      目前,大部分的RTC场景都不是WebRTC那样的P2P模式,而是存在转发服务器的SFU场景。这类场景无法像P2P场景那样直接在双方的交互上做工作,而是要考虑上行场景以及下行场景的不同。
    在这里插入图片描述

      (客户端 ——> 服务器),这条上行链路上,我们客户端可以做的事情比较多。
        上行网络异常,我们客户端可以进行码率自适应、FEC、NACK等方式去适配我们的上行弱网情况。
      (服务器 ——> 客户端)在下行链路时。
        我们服务器为了保证实时性,不会对数据包进行编解码操作,因此是无法实现无损降码率的。这两种场景下,我们在带宽算法的选择上就出现了差异。

    音视频混合场景

      撇开上行网络不谈,我们强调解决下行网络的弱网情况,那么可以明确的是,下行网络中必定会使用到丢包补偿策略。因为丢包补偿策略不需要调整码率,只是简单的向服务器要求重传即可。但是这样单一的方式则会造成以下缺陷:带宽受限时传输直接崩溃。因为在带宽受限时下行的客户端并不知道网络传输受限,因此不断要求重传导致了进一步加重网络拥塞,直到完全崩溃。因此在下行服务器部分引入一个带宽估计算法是必不可少的。
      当前场景我们定位为音视频混合的场景,可知音频数据占用带宽与视频数据占用带宽相比小很多,同时音频数据粘性比视频要低(这里理解为视频关键帧数据需要多个数据包组成一个帧,任何一个包丢失都会造成数据失效,因此粘性高)。在此我们首先要保证在整个传输链路中占比最大的、粘性最高的数据要在我们尽量可控范围内进行传输。因此我们针对视频数据进行了带宽估计以及发送控制。

      BBR带宽估计算法在下行网络中的可行性:
      1.即时下行带宽测算:
        我们下行网络能做的,就是对下行网络进行准确的带宽统计并反馈给上行去进行码率调整,实现下行抗弱网,BBR的带宽统计比其他的算法更符合实际带宽情况;
      2.控制发送速率:
        在反馈给上行的同时,还需要对下行发送数据的控制,把我们发送到网络的数据实现可控。这里不但要把正常的传输数据包进行限制,连带重传的数据包也需要进行进一步的控制;
      3.激进的带宽算法:
        下行网络与上行不同,目前家用下行网络普遍带宽高于上行,因此我们需要在拥塞解除后立刻进行带宽抢占,而BBRV1的激进做法非常适合。
      4.音频抢占问题:
        BBR算法提供了比较强力的带宽抢占能力,因此在与裸流的竞争中不至于过于被动,而我的音频流为无控制的数据流,因此BBR相比来说优势更大。

    3.2 实现思考

      在RTC实现中,我们优先考虑到数据转发的性能影响,因此大部分的SFU服务器都是多进程——单线程模式,因此在BBR的计算中不能引入过多的时间消耗,从而影响整体的传输效率。
      在使用RTP协议进行传输时,我们为了保证实时性时长使用的NACK这样的补包策略——也就是在出现网络丢包时再通知对方补发而不是像TCP协议中持续的ACK来保证数据的可靠性。而这里引入的quic协议中的BBR则依赖于持续的ACK来实现的,因此在实现中做了大量的适配。
      1.ack策略改为100ms内统一回复这段时间内接到的包以及数据停留时长。
        这样的方式不会引入过于频繁的确认操作,但是会引入一些确认问题:当ack信令丢失时会出现大量的未确认情况、同时确认滞后。而且RTT的计算需要在服务端进行,RTT = 当前的确认时间 - 停留时间 - 发送时间;
      2.反馈策略为200ms对上行进行一次带宽反馈。
        BBR的带宽估计下降非常迅速,因此我们不但在反馈上行时故意调低了反馈带宽(真实带宽 * 0.9)来应对波动,同时还加入了物理丢包补偿,实现在400ms内数据迅速准确下降;
      3.物理丢包补偿与补包后丢包参考
        在实现中,我们加入了两个丢包参考量。我们都知道BBR在抗丢包上几乎是毫无措施,因此我们需要在外层做丢包策略。首先是补包后丢包:意思是经过重传后的丢包率,实际反映了下行客户端有效数据接收量。这里我们引入了google的丢包带宽估计算法,应对带宽稳定时额外的丢包率问题;其次是物理丢包率,我们同时还统计了下行接收的情况以及上行发出的情况,来计算准确的物理丢包率,这样可以准确的添加到BBR的增益中,同时反馈上行时故意降低发送带宽,为我们下行重传腾出足够的带宽空间。
      4.发送数据策略
        首先发送数据是一个比较头疼的问题,因为BBR输出的Pacing_Rate是一个带宽值,也就意味是两个变量组成:数据量和时间。在发送时我们要怎么去界定是控制发送间隔还是控制发送数据量的多少呢?我们先解释两种做法的不同。首先,控制发送间隔调整数据量,这样的做法很有可能会导致我们发包的数据不够均匀,同时当网络极差的时候可能会有多数间隔会无法发送数据。其次,控制发包数据调整间隔,这样的做法则是在发送时无法可靠控制发送间隔,导致下行接包忽快忽慢,最致命的是在网络剧烈抖动时间隔相差可能很大,不利于下行处理。因此我们此处把发送间隔根据RTT的状态进行切换,同时在切换的过程中根据对应的间隔去调整发包量,这样在稳定的间隔内控制发包数据则会更加平滑。不仅如此,我们还会让每一个间隔内必须要发送一个数据包,即使现在的网络已经较差,这样的做法是为了保证每一个间隔都有数据流出,不至于激增我们的延迟。
      5.丢帧策略
        在应对1v1场景时,我们可以肆无忌惮的反馈给上行去调整码率,但是在多人的下行网络中,我们是不可能因为其中一人的问题而牺牲所有人的体验,因此,多人部分在服务端降码率的方式只能是把不重要的帧丢掉而实现降码率。这样的方式是要结合自身的业务场景,尽量保证I帧的发送,把不必要的P/B帧丢掉来降低码率。

    四、总结

      上面对常用的抗弱网方式进行了简介,同时着重介绍了BBR拥塞控制算法,最后在简单总结了近期使用BBR算法来实现下行弱网优化的关键点,以后有时间将会继续拓展这部分的内容。

    展开全文
  • CHM-1400/1700/1900伺服传动电脑高速高精密卷筒纸分切机、自动光标跟踪型分切机、卡纸型分切机、厚薄两用型分切机,广泛适用于卡纸、铜版纸、牛皮纸、文化纸、双胶纸、高光相纸、金银卡纸、铝箔纸、激光镭射纸、烟酒...
  • 漫谈TCP BBR正当时

    千次阅读 2021-03-06 06:52:46
    上周随意发的一篇朋友圈,引出本文: 但凡有信道仲裁的地方就不能用self–clock,这就跟我之前说的很多vpn是半双工处理一样,这是wifi,xG的根本问题,用pacing代替burst,这..."pacing_rate is BBR’s primary contr
  • BBR

    千次阅读 2019-04-22 15:55:00
    最近,Google 开源了其 TCP BBR 拥塞控制算法,并提交到了 Linux 内核,从 4.9 开始,Linux 内核已经用上了该算法。根据以往的传统,Google 总是先在自家的生产环境上线运用后,才会将代码开源,此次也不例外。根据...
  • bbr.sh一键服务器对于延迟加速,针对国外服务器的延迟,安装bbr脚本,
  • Google's BBR拥塞控制算法模型解析_Netfilter,iptables/OpenVPN/TCP guard:-(-CSDN博客_bbr拥塞控制算法; 笔记: 全文的大意: BBR的模型设计是基于延时策略的,完全不同于之前一直基于丢包策略判断网络的,比如...
  • 拥塞控制算法——BBR

    千次阅读 2021-05-24 18:34:21
    拥塞控制算法——BBR 目录 BBR产生的背景 TCP算法存在的问题 BBR算法的特点及核心 BBR算法基本原理 BBR结构图 即时带宽的计算 BDP BBR状态机 BBR算法的优缺点 抗丢包能力强 低延迟/抢占能力强 平稳发送 ...
  • 译者序本文翻译自 Google 2017 的论文:Cardwell N, Cheng Y, Gunn CS, Yeganeh SH, Jacobson V.BBR: congestion-...
  • Ubuntu开启BBR加速

    千次阅读 2021-11-25 23:46:01
    新的 TCP 拥塞控制算法 BBR (Bottleneck Bandwidth and RTT) 可以让服务器的带宽尽量跑慢,并且尽量不要有排队的情况,让网络服务更佳稳定和高效。 Linux Kernel 内核升级到 4.9 及以上版本可以实现 BBR 加速,由于...
  • Google的TCP BBR拥塞控制算法深度解析

    千次阅读 2021-09-23 02:34:41
    原作者:dog250,授权发布重新整理:极客重生hi ,大家好,今天推荐一篇我认为在TCP BBR技术里面分析非常透彻的文章,希望大家可以学习到一些真正的知识,理解其背后的设计原理,才能应...
  • 最近在排查网络问题时,发现在服务器上部署了Linux 4.9 的 TCP BBR拥塞控制算法以后,访问速度得到了成倍的提示,顿时觉得⼗分神奇,在⽹上查询了BBR相关资料,阅读了BBR的论⽂,下⾯基于该论⽂向⼤家简要分析⼀下...
  • 如何解决TCP BBR的RTT不公平性问题

    千次阅读 2021-08-12 19:49:05
    首先看下BBR的RTT不公平是什么。 设λ(t)\lambda(t)λ(t)为时间ttt时刻一条BBR流的测量速率,根据范雅各布森管道Bottleneck通量原理,它的值等于BDPRTT\dfrac{BDP}{RTT}RTTBDP​: λ(t)=BDPRTT\lambda(t)=\dfrac{...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 4,389
精华内容 1,755
关键字:

bbr