2. 流控基础概念

流量控制里,令牌桶算法是个绕不开的基础。说白了,它就像个交通管制员,决定你的数据包什么时候能走,什么时候得等着。我刚开始接触流控时,总觉得这玩意儿抽象,后来在项目里调过几次参数,才真正摸透了它的脾气。

2.1 令牌桶的核心原理

想象一下,你手里有个桶,桶里不断往里加令牌。每个数据包要发出去,都得先拿走一个令牌。桶满了,令牌就溢出了,不再增加。这就是令牌桶的基本逻辑。

核心机制:

  • 令牌生成速率(rate):每秒往桶里加多少个令牌,决定了平均流量上限
  • 桶容量(burst size):桶最多能存多少个令牌,决定了突发流量的大小
  • 数据包处理:每个包消耗一个或多个令牌,桶里有令牌就放行,没有就丢弃或排队

为什么会这样设计?你想想看,如果只限制平均速率,那网络流量会像心电图一样剧烈抖动。有了桶容量,就能吸收短时间的流量尖峰,让网络更平滑。

我的经验: 有一次调视频会议系统的流控,我发现平均速率设对了,但用户总抱怨画面卡顿。后来查出来是桶容量太小,视频的I帧(关键帧)一来就把桶打穿了。把burst size从10KB调到50KB后,问题就解决了。

2.2 参数配置:速率与突发大小

配置令牌桶,其实就是调两个参数:速率(rate)和突发大小(burst size)。这两个值怎么配,直接决定了流控效果。

参数 含义 单位 典型场景
rate 令牌生成速率 bps / pps 限制平均带宽
burst size 桶最大容量 bytes / packets 允许突发流量

举个例子。你给一个用户限速1Mbps,burst size设成100KB。正常情况下,流量被限制在1Mbps以内。但如果用户突然发一个200KB的数据包,桶里只有100KB的令牌,那这个包就会被丢弃或排队。嗯,这里要注意,burst size不是越大越好,太大了会失去限速意义,太小了又容易丢包。

避坑指南: 我曾经在一个数据中心项目里,把burst size设得特别大,想着让用户偶尔能爽一把。结果几个用户同时突发,直接把上行链路打满了,其他业务全受影响。后来我学乖了,burst size一般设成rate的1-2秒的流量,既允许小突发,又不至于失控。

2.3 单桶模型 vs 双桶模型

单桶模型是最基础的实现,一个桶管所有流量。但实际网络中,我们经常需要区分不同优先级的数据。这时候双桶模型就派上用场了。

2.3.1 单桶模型

单桶模型就是上面说的那个桶。所有数据包共用同一个令牌池,先到先得。优点是简单,缺点是没法区分优先级。

  • 优点:实现简单,CPU开销小
  • 缺点:高优先级流量和低优先级流量抢同一个桶,关键业务可能被挤掉
  • 适用场景:单一业务、对优先级不敏感的网络

2.3.2 双桶模型

双桶模型有两个桶:C桶(承诺桶)和E桶(超额桶)。C桶装的是承诺带宽的令牌,E桶装的是超额带宽的令牌。数据包先消耗C桶的令牌,C桶空了再消耗E桶的令牌。两个桶都空了,包就被丢弃。

双桶模型的关键参数:

  • CIR(承诺信息速率):C桶的令牌生成速率,保证的带宽
  • CBS(承诺突发大小):C桶的容量,保证的突发
  • EIR(超额信息速率):E桶的令牌生成速率,允许超额的带宽
  • EBS(超额突发大小):E桶的容量,允许超额的突发

双桶模型在运营商网络里用得特别多。比如你买了一个100Mbps的宽带,CIR就是100Mbps,CBS设成1MB。如果偶尔跑到120Mbps,只要E桶有令牌,也能通过,但会被标记为低优先级。网络拥塞时,这些超额流量会被优先丢弃。

我的建议: 配置双桶时,CBS和EBS不要设得一样大。我一般把CBS设成CIR的1-2秒流量,EBS设成EIR的0.5-1秒流量。这样既能保证承诺带宽的稳定性,又能给超额流量留点余地。

2.4 令牌桶的数学模型

搞懂了原理,咱们看看数学怎么描述。令牌桶的运作可以用一个简单的公式表示:

// 令牌桶状态更新(每t秒执行一次)
bucket = min(bucket + rate * t, burst_size)

// 数据包处理
if packet_size <= bucket:
    bucket -= packet_size
    放行
else:
    丢弃或排队

这个模型里,bucket是当前桶里的令牌数,rate是生成速率,burst_size是桶容量。每次加令牌时,不能超过桶容量。每次发包时,桶里令牌够就减掉,不够就处理。

实际项目中,我常用这个模型来估算流量行为。比如一个视频流,平均码率2Mbps,峰值码率10Mbps,持续100ms。如果rate设成2Mbps,burst size至少得设成10Mbps * 0.1s = 1Mb = 125KB,才能保证不丢包。

2.5 令牌桶的SVG流程图

下面这张图展示了单桶和双桶模型的核心流程,我画出来方便你对照理解。

令牌桶模型流程图 单桶模型 令牌生成器 (rate) 令牌桶 (burst size) 数据包处理 双桶模型 令牌生成器 (CIR+EIR) C桶 (CBS) 承诺带宽 E桶 (EBS) 超额带宽 数据包处理 说明:单桶模型所有流量共享一个令牌池,双桶模型将流量分为承诺和超额两部分 数据包优先消耗C桶令牌,C桶耗尽后再消耗E桶令牌

2.6 单桶与双桶的对比

为了让你更直观地理解两者的区别,我整理了一个对比表。

对比维度 单桶模型 双桶模型
令牌池数量 1个 2个(C桶 + E桶)
优先级区分 不支持 支持(承诺 > 超额)
实现复杂度 低 中
适用场景 简单限速、单一业务 QoS保障、多业务混合
典型参数 rate, burst size CIR, CBS, EIR, EBS

注意: 双桶模型虽然功能更强,但配置不当反而会出问题。我曾经见过一个案例,运维把CIR设成100Mbps,EIR设成200Mbps,结果用户流量全跑到E桶里去了,承诺带宽形同虚设。原因是他把CBS设得太小,C桶经常空着,流量只能走E桶。所以配置时一定要保证CBS能覆盖正常流量波动。

2.7 实际调试中的参数调优

调令牌桶参数,我一般遵循几个原则:

  1. 先测基线:用流量发生器打一个恒定速率流,观察丢包率和延迟
  2. 再测突发:打一个脉冲流量,看burst size够不够用
  3. 最后调优:根据业务特点微调参数

举个例子。有一次调一个Web服务器的流控,我先把rate设成100Mbps,burst size设成1MB。然后用iperf打流,发现恒定速率下没问题,但一模拟突发请求(比如首页大图加载),丢包率就飙到5%。我把burst size调到2MB后,丢包率降到0.1%。嗯,这里要注意,burst size不是越大越好,太大了会导致延迟增加,因为桶里积压的令牌会让后续流量瞬间爆发。

我的经验: 调参时,我习惯用tc命令在Linux上做快速验证。比如:tc qdisc add dev eth0 root tbf rate 100mbit burst 1mbit limit 2mbit。这个命令配了一个单桶令牌桶,rate 100Mbps,burst 1Mb,队列长度2Mb。改参数时,先tc qdisc del再tc qdisc add,别直接改,容易出问题。

令牌桶算法看着简单,但真正用好它,需要理解业务流量特征和网络环境。单桶模型适合快速限速,双桶模型适合精细化管控。你在实际项目中,可以根据需求灵活选择。