做跨境业务的客户来问 BBR,一年能碰上几十回。网上教程写得跟卖药似的:贴一段一键脚本,说开了之后带宽立刻跑满、网页秒开。我不太爱接这种话——拥塞控制算法压根不是加速开关,它只决定一件事:你的发送端在这一刻敢往网络里塞多少数据。链路给你多少,就是多少,算法变不出带宽来。
那为什么很多人开了确实有感觉?因为传统算法在跨境这种"又长又容易随机丢包"的管道上,天生就会把窗口压着不上来。BBR 换了一套判断依据,把这笔账重新算了一遍。下面我把该讲的、不该吹的、以及它根本管不到的部分,一次说清楚。
先给结论,赶时间的话看这几条就够了:
先说清楚传统算法在干什么。Reno 和后来的 CUBIC 都属于基于丢包的拥塞控制:它们把"丢包"当成"网络堵了"的唯一信号。一旦检测到丢包,就把发送窗口砍下去(Reno 是直接减半),然后以线性或者凸函数的方式一点点爬回来。这个加性增、乘性减的套路,在几十毫秒 RTT 的局域网里还挺好用。
带宽时延积,英文叫 Bandwidth-Delay Product,简写 BDP,算法很简单:带宽乘以往返时延。它表示的是"这条管道里正在飞的数据量"——也就是要把管道填满,发送端至少得有这么多的未被确认数据在途。举个例子算一遍:带宽 100Mbps、RTT 200ms,那么 BDP 就是 100,000,000 bit/s 除以 8 得到约 12.5MB/s,再乘以 0.2 秒,约等于 2.5MB。意思是这条路上一时刻得有 2.5MB 的数据在飞着,才算把带宽吃满。
问题出在"爬"这个动作上。窗口是按 RTT 一轮一轮往上加的,RTT 越大,同样时间内能加的轮次越少。RTT 200ms 的跨境链路,一秒只有 5 个往返;RTT 1ms 的同机房链路,一秒有 1000 个往返。同样被砍一次窗口,前者要花两百倍的时间才能爬回原来的高度。这就是为什么很多人看着监控一头雾水:带宽明明买了 100M,实际长期跑在很低的位置,还忽高忽低——不是带宽没给你,是窗口根本没机会爬上来。
更要命的是丢包这件事本身。丢包不等于拥塞,这是理解整套问题的关键。链路本身有随机误码、中间穿过无线段、某台设备缓冲区很浅,这些都会丢包,但它们跟"这条路上现在车太多了"完全是两回事。
基于丢包的算法没有别的判断手段,只能一刀切:看见丢包就当成拥塞,砍窗口。于是随机误码这种"假信号"也被当成"真拥堵"处理,窗口被反复按下去,吞吐就一直贴着地板走。链路越长、随机丢包越多,这个误判被放大的次数就越多——这就是长肥管道上"带宽跑不满"的根源。
还有个容易被忽略的放大器:缓冲区浅的设备。缓冲区浅意味着队列还没堆积起来就开始丢包,而基于丢包的算法恰恰要等丢包才减速,两者凑到一起的结果是——还没真正把带宽吃满,就被迫开始减窗。你要是见过那种"带宽利用率常年上不去、抖动还特别大"的跨境监控曲线,多半就是这个组合在作怪。反过来,缓冲区很深的设备虽然不容易丢包,但代价是排队延迟被堆得很高,等你察觉到丢包的时候,队列里已经积压了一堆数据。深也不是,浅也不是,这就是基于丢包这套逻辑的结构性难题。
BBR 是 Google 提出来的,名字取自 Bottleneck Bandwidth and Round-trip propagation time 两个词的开头字母。它的核心转变是:不去猜丢包,改成去测量。它不再把丢包当成主要信号,而是估计这条链路的两个物理量,用这两个量去算该发多少。
第一个是 BtlBw,瓶颈带宽,也就是整条路径上最窄那一截的传输能力。测量方法是观察一定窗口内送达速率的最大值——因为带宽是链路的物理属性,再怎么发也不可能超过它,所以观测到的最大送达速率就是它的估计值。
第二个是 RTprop,往返传播时延,也就是不包含排队在内的那条"纯净"时延。怎么把排队排除掉?取一段时间内观测到的最小 RTT。队列空的时候,观测到的 RTT 最接近纯传播时延;队列一堆积,RTT 就被抬上去了。所以最小值就是它的估计。
这两个数一乘,就是 BDP。BBR 拿这个 BDP 来指导发送量——不是"没丢包就加一点",而是"我算出来这条管道能装这么多,我就按这个量发"。
这里要讲清楚一个前提:这两个数是不太可能同时测准的。想测准最小 RTT,就得把发送量压低让队列空着;想测准瓶颈带宽,就得把管道发满让队列有点积压。两个目标本身互相拉扯,所以 BBR 的做法是交替进行——大部分时间按当前的估计值发送,周期性地上探一次带宽、周期性地探测一次最小 RTT,用新观测到的值去更新估计。理解这一点很重要,后面讲"为什么会有速率抖动"就是从这来的。
由此带来两个实际效果,也是大家真正能感知到的部分。
第一个是吞吐。既然发送量来自对带宽和时延的测量,而不是对丢包的反应,那么浅缓冲区、有随机丢包的链路上,它不会被那些"假拥堵信号"牵着走,吞吐自然更好。这正是跨境场景最吃香的那一类链路。
第二个是延迟。基于丢包的算法天生倾向于把缓冲区填满——填满了才会丢包,丢了包才知道踩了刹车,这个机制本身就意味着长期排队。BBR 按 BDP 发送,目标是刚好把管道填满,而不是把中间设备的缓冲区也填满,所以排队延迟更低。很多人开了 BBR 觉得"网页变快了",变快的主要是这部分排队延迟,不是带宽变大了。
该不该开,本质是四个维度交叉判断:RTT 高不高、丢包是随机的还是真堵、缓冲区深不深、出网有没有被限速。我把常见组合列成一张表,对着自己的情况找位置就行。
| 链路特征 | 丢包性质 | 期望收益 | 注意事项 | 是否建议先试 |
|---|---|---|---|---|
| 跨境/跨洲,RTT 高、BDP 大 | 随机丢包为主(误码、浅缓冲) | 吞吐与排队延迟都有改善空间 | 先确认内核有 tcp_bbr、qdisc 支持 pacing | 建议先试 |
| 同机房/同城,RTT 很低 | 基本不丢包 | 收益通常有限 | 别把优化精力放错地方,先查应用与链路 | 不建议优先折腾 |
| 跨境但缓冲区深、丢包极低 | 几乎不丢包 | 相对 CUBIC 的优势可能不明显 | 必须做前后对比,别凭感觉下结论 | 可以试,别期待奇迹 |
| 出网方向被限速或被清洗设备管着 | 限速/清洗导致的丢包 | 算法层面没有解法 | 先解决限速与清洗策略,再谈算法 | 先解决限速 |
| 小包为主、对延迟极度敏感的实时业务 | 少量丢包 | 吞吐可能改善,抖动未必改善 | 盯 RTT 分布而不是均值,看 P99 | 谨慎,逐项观测 |
| 容器环境,共享宿主机网络栈 | 取决于宿主机 | 容器内改不了宿主机的算法 | 先确认宿主机现状,再决定要不要动 | 需平台确认后 |
表里最该记住的是最后两行之外的那条判断:出网方向被限速或被清洗设备管着的时候,任何拥塞控制算法都救不了你。限速是别人给你设的天花板,你换一种爬墙的姿势,天花板还在那儿。这类情况先去查限速策略、清洗阈值、带宽配额,把这些理顺了再来谈算法。
别上来就跑一键脚本。先花两分钟把现状查清楚,很多情况下你会发现——早就开了。
BBR 需要较新的内核支持,对应的内核模块叫 tcp_bbr,从 4.9 开始进入主线。注意,进了主线不等于行为就稳定了:早期版本和后续版本的实现差异不小,不同内核版本之间迭代了很多次。这也是为什么网上有些经验贴相互打架——他们跑的内核根本不是一回事。查模块和可用算法最直接的办法是看这个文件:
cat /proc/sys/net/ipv4/tcp_available_congestion_control
这一行会列出当前内核可用的拥塞控制算法,里面有没有 bbr,一眼就能看到。
默认值在 /proc/sys/net/ipv4/tcp_congestion_control 里。不少发行版和云平台的镜像早已默认启用 BBR,尤其是面向跨境场景优化过的镜像。查完再动手,省得折腾半天发现原地转了一圈。
这一条最容易被跳过,也最容易踩坑。BBR 的发送是按测量出来的速率做 pacing(匀速打散发送)的,它需要 fq 这类 pacing 友好的 qdisc 才能真正把这个 pacing 效果发挥出来。队列规则不对,BBR 的节奏就打不出来,效果打折扣。用 tc qdisc 相关的命令(比如 tc qdisc show)可以看到当前网卡上的队列规则。我只讲这一句事实:BBR 需要 pacing 友好的 qdisc。至于 qdisc 本身怎么调,那是另一个话题,这篇不展开。
容器共享宿主机的网络栈,改不了宿主机的拥塞控制——在容器里折腾内核参数是白费力气。云主机则要看平台和镜像:部分云平台镜像已默认启用,也可能压根没给你改内核参数的口子。动手之前先确认现状,确认不了就发工单问清楚。
跨境业务挑节点,我更在意这条链路本身走的是哪条回国路径,算法是第二步——一万网络 BGP 多线加 CN2 GIA 回国这条底子打好了,再去谈 BBR 才划算。
只讲好话的技术文章不值得信。BBR 的争议我按自己的判断排个序,一条条说。这几条不是为了劝退你,是为了让你开完之后遇到怪现象时,能立刻反应过来"这是设计使然"还是"这是我这儿的问题"——分不清这两者,就会在错误的地方反复折腾。
公平性争议是第一位。同一条瓶颈链路上,BBR 流和基于丢包的 CUBIC 流抢带宽时,BBR 可能挤占 CUBIC 流的份额。原因不难理解:CUBIC 靠丢包减速,BBR 不把丢包当主要信号,两边对"什么时候该让"的回答不一样。互联网上大量流量还在跑 CUBIC,这不是实验室里的假想问题。我的立场是:在你能掌控的边界内开,不值得为此焦虑,但也别指望瓶颈链路上所有人都守同一套规矩。真遇到共享瓶颈上被挤的业务,解决办法从来不是算法,是带宽和隔离。
版本迭代带来的行为差异,比很多人想的影响大。BBR 从进入主线之后一直在改,不同内核版本上的实现细节差异不小。你在一台机器上跑出来的结论,换一台内核版本不同的机器未必成立。所以任何"我测了,效果很好/很差"的说法,都得带上内核版本一起看,否则没有参考价值。
探测机制造成的速率抖动,是设计的一部分,不是 bug。BBR 会周期性地上探带宽——ProbeBW 阶段用增益去试探"是不是还有更多带宽可用",探完了再收回来。这个动作必然带来一定的速率抖动。对大文件传输这类吞吐型业务,这点抖动无所谓;对延迟极度敏感的小包业务,未必受益,甚至可能觉得更毛躁。所以别用"开完感觉更抖了"就断定开错了,先看你业务是哪种类型。
优质链路上收益不明显,这话也得说。深度缓冲区加上极低丢包的链路,本来就没让基于丢包的算法吃亏,BBR 相对 CUBIC 能拿到的额外收益可能并不明显。这时候开的理由是"没有明显坏处",不是"能提速"。把预期摆正,免得回头失望。
BBR 的边界必须画清楚,不然出了问题你会查错方向。
只作用于 TCP 发送侧。开了 BBR 的机器作为发送方时,按 BBR 的算法发数据;对端用什么算法你管不着,中间链路是什么设备你也管不着。它对下载方向——也就是本机作为接收方的方向——没有直接帮助。指望开了 BBR 让本机下载变快,方向就搞反了。
UDP 不受它约束。BBR 是 TCP 的拥塞控制,UDP 不走这套。实时音视频的丢包、卡顿,要从应用层的码率自适应、抖动缓冲、前向纠错这些地方去解,跟内核的 tcp_congestion_control 一点关系都没有。
QUIC/HTTP/3 也管不到。QUIC 自带拥塞控制,常见实现用 CUBIC 或者 BBR,它跑在 UDP 之上,内核的 tcp_congestion_control 这个开关根本碰不着它。要让 QUIC 侧换算法,得在应用或者服务端自己的配置里改,不是改内核参数。
中间设备的限速、清洗策略,它绕不过去。出网被限速、被清洗设备管着的场景,算法层面没有解法,先解决限速。
DNS 解析和应用层耗时,跟它无关。解析慢、TLS 握手慢、后端处理慢、数据库查询慢,这些时间不发生在传输窗口里,开了 BBR 也不会动。判断慢在哪一层,永远比盲目开开关重要。
这里顺便提一句另一件常被混为一谈的事:握手队列。有人把"连接建立慢、并发上不去"也归到拥塞控制头上,其实那属于监听侧的队列参数范畴,跟传输过程中的发送窗口是两码事。这篇只讲数据在管道里怎么发,握手那一层的问题另说。
改之前和改之后各测一轮,其余条件不动:同一时段、同样路径、同样的对端。跨境链路的拥塞状况跟时间段强相关,白天测一次、凌晨改完再测一次,两个数字放一起没有任何意义。
要对比的指标就三项:吞吐(实际跑出来的传输速率)、RTT 分布(不要只看平均,看 P95、P99 和抖动范围)、重传率。这三项一起看才能判断到底发生了什么:吞吐上去了但重传率也上去了,说明你只是在硬挤;RTT 的尾部压下来了,才是排队延迟真的改善了。
iperf 这类工具可以用来做对照实验,但单次 iperf 结果说明不了什么。跑一轮、多跑几轮、换几个时段跑,看到稳定的趋势再下结论。别拿一张截图当结论发群里,那是给自己挖坑。
还有一点,别只盯着吞吐看。有些业务的改善主要体现在"高峰期不再那么卡",也就是尾部的延迟被压下来了,平均吞吐反而变化不大。这类收益用平均数是看不出来的,必须看分布。同时把这些指标接到日常监控里,出事的时候才有历史数据可以对照,没有基线的排查全靠猜。
网络层的数字好看,不等于业务真的变好了。真正该盯的是业务侧那几个指标:接口响应时间分布、上传/下载的完成耗时、超时与重试次数、用户侧的首包时间。这些指标贴近真实体验,也比吞吐更能说明问题。
观测周期也别太短。跨境链路的拥塞本身有周期性,节前、促销、晚高峰都会让链路状态变化。我的习惯是:改完之后先连续看一到两周,覆盖几个典型的高峰时段;有条件的再多看一个月,看有没有出现当时没预料到的抖动。只看改完那十分钟,等于没看。
坑一:把 BBR 当加速器,期待带宽变大。坑在于预期错了——它改变的是发送策略,不是链路的物理能力,带宽该是多少还是多少。怎么避:开之前先明确"我要解决的是窗口爬不上去,还是排队延迟高",带着具体问题去验证,别指望一个开关解决所有问题。
坑二:只看内核模块加载了,没看 qdisc。坑在于 BBR 需要 fq 这类 pacing 友好的 qdisc 才能发挥 pacing 效果,队列规则不对,节奏就出不来。怎么避:改完之后用 tc qdisc 确认网卡上的队列规则,把这一项一并检查。
坑三:在容器里折腾宿主机的参数。坑在于容器共享宿主机网络栈,改不了宿主机的拥塞控制,你在里面改半天可能毫无效果,甚至报错让你以为配置失败。怎么避:先确认运行环境到底是物理机、云主机还是容器,容器场景去找平台确认宿主机现状。
坑四:一次 iperf 就下结论。坑在于单次测量受当时链路状态影响太大,随机性远大于算法带来的差异。怎么避:固定时段、固定路径、多轮多次,看趋势而不是看某一个数字;同时把 RTT 分布和重传率一起记下来。
坑五:限速没解决就先怪算法。坑在于出网被限速、被清洗设备管着的时候,丢包是限速造成的,换算法毫无用处。怎么避:先确认带宽配额、限速策略、清洗阈值,把这些排除了再谈拥塞控制,顺序反了就是浪费时间。
先别急着怀疑配置。最常见的三种情况:一是你的链路本来就不在 BBR 的收益区间——同机房、同城、RTT 很低又不丢包,BBR 相对 CUBIC 的收益本来就可能不明显,这时候"没感觉"是正常的;二是你测的是下载方向,而 BBR 只作用于 TCP 发送侧,对接收方向没有直接帮助;三是你的瓶颈根本不在传输层,是 DNS、TLS 握手、后端处理或者出网限速在拖后腿。判断方法很直接:查 /proc/sys/net/ipv4/tcp_congestion_control 确认真的生效了,再做同一时段前后的吞吐与 RTT 分布对比。对比下来没差异,说明这条链路不需要它,回退就行,不用纠结。
会改变发送行为,所以要分开看。好处是它倾向不填满缓冲区,排队延迟更低,多数业务的实际体验是变好或者持平。需要留意的是它的探测机制:ProbeBW 阶段会周期性上探带宽,带来一定速率抖动,对延迟极度敏感的小包业务未必受益。另一个现实是与基于丢包的算法共存时的公平性争议——同一条瓶颈链路上,BBR 流可能挤占 CUBIC 流的带宽,这个影响是对外的,不局限于本机。建议做法:先在非核心业务或者灰度的一批机器上开,观察一到两周,确认吞吐、RTT 尾部、重传率三项都稳定再全量。
严格说,容器里改不了宿主机的拥塞控制——容器共享宿主机的网络栈,你在容器内折腾内核参数,动的不是你想动的那一份。所以答案是:能不能开取决于宿主机,而不取决于你在容器里执行了什么。实操上分三步:先确认宿主机内核有没有 tcp_bbr 模块、当前默认算法是什么、网卡上的 qdisc 是不是 pacing 友好的类型;这三件事都在宿主机上查,容器里看到的结果未必是你以为的那个。托管类容器平台一般不给改宿主机参数的权限,这种情况只能找平台确认现状,或者换用能自己控制内核参数的云主机/裸金属。
不一定,先分辨抖动的类型。BBR 的 ProbeBW 阶段会周期性上探带宽,探完再收,这个动作本身就是设计的一部分,必然带来一定的速率抖动——这是"探测抖动",不是故障。判断依据是看你的业务形态:大文件传输、批量同步这类吞吐型业务,这点抖动基本无所谓;小包、交互频繁、对延迟极度敏感的业务,可能就觉得更毛躁了。另外要看的是 RTT 的分布而不是均值:如果平均 RTT 差不多但 P99 明显抬高了,那确实是负收益,可以考虑回退;如果只是速率曲线看起来不平滑,而吞吐和尾部延迟都在改善,那属于正常现象,继续观察即可。
关系很直接:回源对源站来说就是一次大流量的 TCP 发送,源站作为发送方,用的就是自己的拥塞控制算法。所以源站的机器上开 BBR,影响的是源站往外发的这一段——如果源站到 CDN 节点之间 RTT 高、有随机丢包,这一段是有可能吃到收益的。但要注意两件事:一是 CDN 节点回源时,源站是发送方,方向别搞反;二是用户到 CDN 边缘那一段,跑在 CDN 自己的网络上,你在源站改参数影响不到。判断有没有效果,还是老办法:固定时段、固定路径对比吞吐与 RTT 分布,别拿用户侧的体验变化直接归因到源站算法上,中间隔了太多环节。
先看现状,再决定动不动手。部分云平台的镜像已经默认启用 BBR,你上去一查发现早就开着,那就不用折腾。查的办法是看 /proc/sys/net/ipv4/tcp_available_congestion_control 里有没有 bbr,以及 tcp_congestion_control 当前默认是什么。需要自己配的话,两个前提缺一不可:内核里有 tcp_bbr 模块(4.9 起进入主线,但不同版本实现差异不小),以及网卡上的 qdisc 是 fq 这类 pacing 友好的类型。这两件事都属于宿主机/内核层面的配置,托管程度高的云主机可能不给权限,确认不了就发工单问清楚,别自己硬改。
涉及具体参数的时候,别信教程,去查你自己的机器和官方文档。内核版本是第一个要确认的——BBR 从 4.9 进入主线,之后实现迭代了很多次,不同版本行为差异不小,脱离版本谈效果没有意义。可用算法列表在 /proc/sys/net/ipv4/tcp_available_congestion_control,当前默认在 tcp_congestion_control,队列规则用 tc qdisc 查看,这三处是最直接的查证入口。更进一步的机制说明、参数含义与版本变更,以所用内核版本的官方文档和发行版说明为准,网上的二手结论只能当线索,不能当依据。
一万网络这边能覆盖的部分,是节点选择、线路质量与工单支持范围内的配置协助:你可以先确认机器所在节点的回国路径与链路质量,再决定算法层面要不要动;云平台权限、镜像是否默认启用、宿主机参数能否调整这类问题,通过 7×24 中文工单确认最快。别自己硬改内核参数改到业务起不来,那笔账不划算。
把 BBR 打开只是动作的开始,判断它值不值得留下来,靠的是后面的观测。我的安排一般是这样:改之前先存一组基线数据,包含吞吐、RTT 分布(含 P95、P99)和重传率;改完之后在同一时段、同一路径再采一组;两组放一起看趋势,而不是看某一个数字的高低。
回退条件要事先写清楚,别等出问题了再临时判断。我的标准很简单:吞吐没有改善且 RTT 尾部明显变差,回退;重传率异常抬升,回退;核心业务的超时与重试次数上升,回退。满足任何一条就退回去,不用犹豫——CUBIC 跑了这么多年,不是不能用。
长周期观测则建议覆盖一到两周的典型高峰,有条件再拉到一个月。跨境链路的状态本身就在变,节前、促销、晚高峰都会带来不同的拥塞形态,只看改完那十分钟等于没看。算法不是一劳永逸的设置,链路变了、业务形态变了,结论就得重新算一遍。
数据来源:文中为公开算法原理与常见内核行为,实际以所用内核版本、发行版与云平台的实际支持为准;具体以签约时最新报价与合同为准。
Copyright © 2013-2020 idc10000.net. All Rights Reserved. 一万网络 科技有限公司 版权所有 深圳市科技有限公司 粤ICP备07026347号
本网站的域名注册业务代理北京新网数码信息技术有限公司的产品