
LTE MAC层令牌桶算法这个话题最早是我在做一轮lte外场测试时被逼着啃下来的。当时的现象很奇怪同一台lte无线路由器用iperf3打上行速率总是稳稳卡在十几兆怎么调天线、换位置、换服务器都没用可换一台手机做终端同样的位置速率却能跑到三十多兆。抓了几次空口日志之后才发现问题根本不在射频也不在核心网而在终端MAC层给每个逻辑信道分资源的那套令牌桶逻辑上。这篇文章就把这套机制从标准文本到落地参数、从公式推导到外场排查完整讲一遍。看完你应该能做到三件事看懂PBR、BSD、Bj这几个参数在干什么自己按业务速率反推出一组合理的配置在外场遇到上行速率上不去的时候知道从哪几个入口去定位是不是令牌桶在捣鬼。文章面向的是做终端、做CPE、做网络优化和路测的同行也欢迎刚接触LTE协议栈的朋友我会尽量把公式掰开揉碎讲。1. 先把位置摆正令牌桶在LTE协议栈的哪个角落1.1 MAC层到底管哪些事很多人一提LTE脑子里第一反应是物理层的OFDMA、MIMO、调制编码或者是RRC的无线承载配置。MAC层夹在中间平时存在感不高但它其实是整个上行方向最忙的一层。它要负责调度相关的HARQ进程管理、随机接入、定时提前量维护、DRX休眠唤醒、缓冲区状态上报BSR、功率余量上报PHR还有我们今天的主角——逻辑信道优先级处理也就是LCP。LCP这件事听起来简单上行资源是基站给的授权一个授权里能塞多少字节是定死的而终端手上同时有一堆逻辑信道排队等着发数据——语音承载、信令承载、默认上网承载、可能还有视频的、游戏的。这些数据要在同一个授权里拼成一个MAC PDU发出去谁先发谁后发谁发多少就是LCP要回答的问题。如果只是按优先级从高到低塞满那问题就简单了。麻烦在于低优先级的信道里可能跑着有保证速率要求的业务如果永远被高优先级挤在后面它会饿死。所以在纯优先级之外必须再加一层速率保证机制。这就是令牌桶存在的根本原因。1.2 两个容易混淆的目的Bj和GBR速率控制标准文本里MAC层的令牌桶实际上以两个面貌出现。一个是在36.321的逻辑信道优先级处理流程里那个反复出现的变量Bj它是所有终端实现都必须做的每个子帧都要算一遍。另一个是在36.300里提到的令牌桶速率控制Token Bucket Rate Control用来约束GBR承载实际能拿到的速率上限防止某个GBR承载因为长期抢占而超额。很多人把这两个当成一回事其实不完全一样但它们的底层模型完全一致都是以固定速率注入令牌、按桶深封顶、消耗令牌才能发送这套逻辑。实际产品里GBR承载的速率控制往往就是靠PBR和Bj这套机制来实现的所以我们在工程上经常直接说MAC层令牌桶。注意PBR和BSD这两个参数只对GBR承载有意义。非GBR承载比如最常见的QCI 9默认上网承载的PBR配置为0BSD也是0Bj恒等于0永远不满足进入第一步的条件。这个结论在外场排查时非常关键后面还会反复用到。如果你接触过Linux的流量控制tc里的tbfToken Bucket Filter用的是rate、burst、limit三个参数思路和这里几乎一模一样。rate对应PBRburst对应桶深limit对应队列长度。做网络运维转过来的同行用这个类比上手会快很多。2. 拆开看标准里的令牌桶到底是怎么运转的2.1 PBR、BSD、优先级这三个参数各自的角色每个逻辑信道在建立的时候网络会通过RRC信令下发一组配置其中和LCP相关的核心是三个优先级priority一个整数数值越小优先级越高。它决定了资源分配时的排序是硬性的先后顺序。PBRPrioritized Bit Rate优先比特速率单位是比特每秒。它定义了这条逻辑信道的保证速率下限注意是下限不是上限。BSDBucket Size Duration桶深时长单位是毫秒。它和PBR相乘得到令牌桶的容量上限。三个参数里优先级负责抢PBR负责保BSD负责能存多少又能爆多少。优先级决定的是顺序PBR决定的是兜底BSD是个调味道具。很多人在外场调参数时只盯着优先级改改完发现没效果就是因为真正卡住速率的那一环在PBR和BSD上。举个生活化的例子帮你建立直觉。想象一个食堂打饭窗口优先级是排队顺序PBR是你每小时能领到的定额饭票BSD决定你最多能攒多少张饭票。你可以今天不用饭票攒着但最多只能攒到上限。等你哪天排到前面了一口气把攒的饭票全花掉这就形成了突发。2.2 Bj的更新公式与桶深的物理意义Bj就是那个饭票余额。它的更新逻辑非常干净/* 每个新传输机会子帧开始时执行TTI 1 ms */ Bj Bj PBR * TTI; /* 正方向封顶负值保留 */ if (Bj PBR * BSD) { Bj PBR * BSD; }第一行的含义很直白每过一个子帧就给这条逻辑信道补充PBR乘以1毫秒的令牌量。如果用字节来理解PBR是比特每秒、TTI是0.001秒乘出来就是每个子帧补充多少比特。第二行是桶深封顶。PBR乘以BSD就是桶的容量。举个例子PBR配128 kbps、BSD配100 ms桶深就是128000乘以0.1等于12800比特也就是1600字节。这条信道最多只能攒1600字节的发送额度攒满了就不再增加了。这就是令牌桶能限制突发规模的根本原因——如果没有这个封顶一条长期没数据的信道会攒下无限额度一旦有数据就会把整条上行链路吞掉。第三点特别注意负值不归零。这是很多人的理解误区。如果某个子帧里这条逻辑信道在第二步后面会讲被高优先级信道挤占导致它实际发送的量超过了它当时拥有的令牌Bj就会变成负数。这个负数代表欠账下一个子帧会先用PBR补账补到正数之前它都没资格进第一步去优先拿资源。这就是令牌桶的惩罚机制也是它能实现公平性的核心。如果你在实现里把负值简单截断成0惩罚就消失了低优先级GBR承载会被无限期压制。2.3 两步分配流程的逐帧推演Bj的值算完之后资源分配分两步走。这两步是整个LCP的精髓我用伪代码写出来更清楚/* ---------- Step 0更新所有逻辑信道的 Bj ---------- */ for (j 0; j num_lc; j) { Bj[j] PBR[j] * 1; /* TTI 1 ms */ if (Bj[j] PBR[j] * BSD[j]) /* 桶深封顶 */ Bj[j] PBR[j] * BSD[j]; } /* ---------- Step 1按优先级服务 Bj 0 的信道 ---------- */ remaining grant_size; for (j in decreasing_priority_order) { if (Bj[j] 0) continue; /* 没令牌跳过 */ served min(remaining, available_data[j], Bj[j]); allocate(j, served); Bj[j] - served; /* 消耗令牌 */ remaining - served; if (remaining 0) break; } /* ---------- Step 2剩余资源按严格优先级瓜分 ---------- */ for (j in decreasing_priority_order) { if (remaining 0) break; served min(remaining, available_data[j]); /* 不限令牌 */ allocate(j, served); Bj[j] - served; /* 可能变负形成欠账 */ remaining - served; }第一步的逻辑是有令牌的信道优先吃但吃多少受令牌余额限制。一条优先级较低但有GBR保证的信道只要它的Bj是正的就能越过那些Bj为负的高优先级信道先拿到资源。这就是PBR保证速率的实现方式——它不改变优先级顺序它改变的是参与排序的资格。第二步的逻辑是令牌用完了也没关系剩下的资源按严格优先级分。这一步是纯抢占谁优先级高谁拿不给任何保证。在线路空闲、资源充裕的时候第二步就能把所有排队数据都发出去GBR承载自然也能拿到超过PBR的速率。理解了这两步你就能解释很多外场现象了。比如高优先级信道的数据量刚好等于或超过PBR时低优先级GBR信道就只有靠第二步的残羹冷炙活着如果此时总流量接近上行峰值残羹几乎没有低优先级GBR信道的实际速率就会稳定卡在PBR附近上下浮动很小。3. 参数不是拍脑袋定的从业务速率反推PBR和BSD3.1 先算业务的真实空口需求配PBR最容易犯的错误是直接拿业务的应用层码率去填。比如语音业务AMR-WB的载荷速率是23.85 kbps有人就直接把PBR配成24 kbps结果发现通话质量时好时坏。问题出在忽略了封装开销和重传。以窄带AMR或者宽带AMR为例一个典型的语音包是这样的载荷20到60字节外面套RTP头12字节、UDP头8字节、IPv4头20字节一共40字节的开销。如果头压缩没生效一个20毫秒周期的包实际要占60加40等于100字节换算下来就是100乘以8除以0.02等于40 kbps。这还没算HARQ重传——上行初始误块率如果按10%算平均还要多出10%的重复传输量实际占用接近44 kbps。再考虑SID帧静默期发送的小包和信令突发的插空PBR至少要留出1.3到1.5倍的余量。所以我一般建议按实际需求的1.5倍左右配置语音业务配到64 kbps是常见做法。当然各家的现网配置不尽相同配置值以运营商和设备商的规范为准我这里给的是反推方法。同样的思路可以用来算视频通话、实时游戏、IoT小包业务。核心公式就一句话空口需求 载荷 协议头开销×1 重传余量× 周期倒数。3.2 BSD的取值区间和它的副作用BSD这个参数比较微妙它单独看没什么意义必须和PBR乘起来看桶深。但BSD本身的选择范围其实是有工程惯例的。BSD太小的直接后果是桶太浅突发能力被压死。比如BSD配10 ms桶深只有PBR的十分之一那么每10毫秒攒的额度只够发一个包的零头稍微有点抖动或者重传令牌就见底了信道只能等下一个子帧慢慢补。表现出来就是速率上不去而且抖动很大。BSD太大也不行。桶太深一条空闲很久的信道会攒下一大笔额度一旦业务突然启动它会在几个子帧内疯狂输出把其他信道的数据全部挤出去。表现出来就是前面几秒速率很猛后面塌下来同时伴随其他业务的短时卡顿。工程上比较常见的取值是50毫秒到200毫秒100毫秒用得最多。这个值的直觉解释是允许业务在100毫秒这个时间尺度上做速率平滑。低于50毫秒平滑能力不足高于200毫秒突发控制太松。3.3 三个典型场景的手算过程我把常见业务的推算过程整理成一张表方便你对照自己的场景改数字。业务类型典型QCI应用层速率含头部开销需求建议PBR建议BSD对应桶深语音AMR-WB123.85 kbps约44 kbps64 kbps100 ms6400 bit / 800 BIMS信令5突发型峰值约30 kbps32 kbps100 ms3200 bit / 400 B视频通话2200 kbps约260 kbps384 kbps100 ms38400 bit / 4800 B默认上网9尽力而为无保证000只走第二步拿语音那行做个完整推导。PBR等于64 kbpsTTI是1毫秒所以每个子帧补充64比特。桶深等于64000乘以0.1等于6400比特也就是800字节。假设一个语音包加上头压缩失效后的总长是100字节那么满桶状态下可以连续发8个包不用等令牌也就是160毫秒的量。这个缓冲深度足以吸收调度抖动和一次HARQ重传同时又不足以让它去抢占别的业务。再验证一下速率保证是否成立。如果系统长期拥塞第一步里每个子帧能给这条信道分配的就是64比特的额度一秒下来正好64 kbps与PBR一致。如果系统空闲第二步会让它发到远超64 kbps的量Bj变负但只要后续有PBR持续补充它总能在下一次拥塞时重新获得最低保障。这就是令牌桶保底不限顶的性质。提示所有PBR之和是有物理上限的。如果所有GBR承载的PBR加起来超过了小区能给这个终端的上行保证速率Bj会长期处于饱和状态此时第一步实际上退化成按优先级全速发送令牌桶名存实亡。配置前先把这个和算清楚。4. 外场测试里令牌桶会露出哪些马脚4.1 上行速率卡在一个奇怪的数值上这是最典型的一种现象。用iperf3打上行速率不是波动而是非常稳定地停在某个值上比如11 Mbps、18 Mbps这种数字而且换个位置、换个服务器都不怎么变。出现这种情况第一反应要去核对这个数值是不是恰好等于某个PBR的倍数或者等于某个PBR的聚合值。判断方法很直接把这条承载的PBR配置查出来。如果实测速率稳定在PBR附近且波动很小同时PHR显示功率还有余量、RSRP和SINR都不差那就基本可以确认是令牌桶在限速而不是空口质量问题。这时候去调天线、加功率都是做无用功。还有一种更隐蔽的情况多个承载同时跑业务时总速率被卡住。这时候要注意是不是某个高优先级GBR承载把第一步的额度吃满了低优先级承载只能靠第二步而第二步的资源又因为总流量接近峰值而所剩无几。4.2 前快后慢的突发形态另一种现象是速率曲线呈锯齿或者前高后低。业务启动的头几百毫秒速率冲到很高然后快速回落并稳定在一个较低的值。这往往和BSD配置过大有关。机理是这样的业务启动前这条逻辑信道的Bj已经攒到了桶深上限。业务一开始它凭这笔积攒的额度在第一步里连续拿到资源速率短时超过PBR。等额度花完Bj变负它就只能在后续子帧里慢慢靠PBR补充速率回落到PBR附近。整个曲线就是一次突发加一个平台。这个行为本身不是故障是令牌桶的设计特性。但如果突发的幅度太大比如桶深配到了PBR的一秒量就会影响到同终端上的其他业务尤其是VoLTE这种对时延敏感的会表现为对方听到断续。4.3 定位工具链与日志看点做外场测试定位这件事工具链我一般备这几样路测软件看RSRP、SINR、PHR、调度次数和MCS业务侧用iperf3或FTP做稳定的打流协议侧抓MAC层日志。前两样保证你知道空口质量没问题第三样才是真正能看到令牌桶的地方。MAC层日志里我重点看三样东西。一是每个子帧的上行授权大小。二是MAC PDU内部各个子PDU的长度和对应的LCID这就是每条逻辑信道实际分到了多少字节。三是BSR上报的内容看看终端上报的缓冲区积压量与实际发出去的量是否匹配。把授权大小和各逻辑信道分到的字节对齐之后你就能反推出理论上的分配比例再和配置的PBR、优先级列表做比对。如果发现某个GBR信道在拥塞时段的分配量明显低于它的PBR对应值那要么是Bj被算错了要么是优先级配置和PBR不匹配。注意BSR上报的是完整缓冲区积压不区分令牌够不够。所以你在日志里会看到某条信道明明积压很多却分不到资源。这不是终端不上报是它在第一步里没有令牌资格。这一点在排查时经常被误读成终端不老实上报。如果没有条件抓终端侧日志还有一条退路在试验环境里搭开源的基站和终端比如srsRAN这类把PBR和BSD做成可调参数自己造拥塞场景跑一轮把不同配置下的速率曲线打出来。这个办法虽然离现网有距离但对理解机制极其有效我自己就是这么把Bj的负值惩罚看明白的。5. LTE无线路由器场景多用户共享下的令牌桶实践5.1 CPE的承载结构与QCI映射现状lte无线路由器也就是常说的CPE它的网络结构和手机不太一样。手机通常是单个用户、单个默认承载加一个IMS承载CPE背后往往挂着几台甚至十几台设备全都要通过同一个上行空口出去。这里有个现实问题绝大多数消费级CPE只建立了一条默认承载QCI为9。这意味着它根本没有配置PBR——PBR等于0BSD等于0Bj恒等于0。这台设备上的所有业务无论是一个视频会议还是一个后台下载在MAC层全都是同一个逻辑信道里的数据没有任何区分。所以当你在一台普通CPE上看到上行速率被卡住首先要确认的是问题到底是不是令牌桶造成的。只有一个逻辑信道的时候令牌桶是不生效的资源分配简化成只要还有授权就把数据发出去。此时的限速要么来自上行峰值终端等级、MIMO层数、调制阶数要么来自网络侧的分组调度或者签约速率限制。真正会用到令牌桶的CPE是那种支持多承载的工业级或者企业级产品。它们会给不同的业务流配置不同的QCI和TFT匹配规则把VoLTE、视频、普通数据分流到不同逻辑信道上然后靠PBR和优先级来实现差异化的保障。如果你手上是这类设备本文前面讲的参数推算方法都可以直接套用。5.2 PBR总和这道算术题多承载场景下最容易翻车的地方是PBR之和不做约束。我遇到过一台设备配了四条GBR承载每条PBR都按业务峰值配得挺漂亮加起来却超过了这个终端在弱场下实际能拿到的上行速率。后果是什么所有承载的Bj长期处于饱和状态第一步里的有令牌变成了无条件成立于是资源分配退化成纯粹的优先级抢占。低优先级GBR承载名义上有保障实际上一点都拿不到因为它们的数据量本身就小抢不过高优先级的大流量。所以配置前必须做一道算术把终端在目标覆盖条件下能达到的上行保证速率估出来再乘以一个0.7左右的系数剩下的额度按业务重要性分配给各条GBR承载的PBR。这个系数留的是给第二步抢占用和调度抖动的空间。5.3 一份可参照的配置与验证流程下面这套流程我在几个CPE项目里用过可以直接参考改数字。第一步确定目标场景的上行能力。选定近点、中点、远点三个测试位置用单承载打流记录各位置的稳定上行速率。第二步按业务的时延和速率要求排优先级。VoLTE最高IMS信令次之实时视频再次然后是普通数据。第三步按前面第3节的方法把每条GBR承载的PBR算出来求和后与第二步得到的速率乘以0.7做比较超出就按比例压缩。第四步BSD统一先配100毫秒跑一轮观察突发现象再针对具体业务微调。第五步验证。验证要做的不是看平均速率而是看三件事拥塞场景下低优先级GBR承载的实际速率是否贴近PBR业务启动时的速率突变量是否可接受VoLTE的丢包和时延是否满足要求。这三件事都过了配置才算站得住。验证项观察方法合格判据GBR速率保障拥塞下打流统计平均速率贴近PBR波动小于20%突发幅度记录业务起头500 ms速率峰值不超过PBR的3倍时延敏感业务同期跑VoLTE统计丢包率无明显断续丢包在可接受范围令牌耗尽表现长时打流观察速率回落拐点拐点位置符合桶深推算值6. 常见问题速查与踩坑记录6.1 问题速查表现象可能原因排查动作上行速率稳定卡在某个值单条GBR的PBR限速核对PBR配置与实测值的对应关系速率前高后低BSD过大桶太深减小BSD观察突发是否收敛低优先级承载完全没速率PBR之和超出上行能力令牌桶失效重新核算PBR总量并压缩有数据却分不到资源Bj为负第一步无资格检查该承载近期是否被长时间挤占单承载CPE上限速令牌桶未生效问题在别处查终端等级、MIMO层数、签约速率语音断续但速率不低桶深过大导致突发冲击降低语音承载的BSD6.2 几个印象深刻的坑第一个坑是把PBR当成限速用。刚开始接触这套机制的时候我以为PBR是这条信道的速率上限后来才明白它只是第一步的额度只要有剩余资源第二步会让它跑得远超PBR。想限速得靠网络侧的调度策略或者签约参数指望PBR限速是错误的期待。第二个坑是在实现里对Bj做了负值截断。有个自研的终端协议栈为了避免出现负数在每次更新Bj的时候加了一行if小于0就置0。结果是低优先级GBR承载一旦被抢占就再也补不回来因为它的欠账被抹掉了下一个子帧又从零开始永远卡在门槛上。改回保留负值之后问题立刻消失。第三个坑是忽略了头压缩状态对PBR需求的影响。同一个语音业务ROHC生效和失效时空口占用差了将近一倍。如果PBR按压缩生效的情况配一旦压缩协商失败语音质量会明显下降。所以配置要按最坏情况来算。第四个坑是并行测试时只看总速率。多条承载同时在跑的时候总速率看着达标但拆开来看高优先级那条吃得过多低优先级的保障根本没兑现。所以验证一定要按逻辑信道拆开统计光看一个总数会掩盖很多问题。我个人在实际操作中的体会是MAC层令牌桶这套机制最难的地方不在公式而在参数之间的耦合关系。PBR决定额度BSD决定弹性优先级决定顺序三者任何一个单独调整都会影响另外两个的表现。所以别指望一次就把参数调到位做几轮对照测试把桶深、优先级、业务组合这几个变量逐个变化记录速率曲线你的手感才会真正建立起来。还有一个建议如果你的环境允许先把Bj的更新过程在自己的代码或者仿真里跑一遍把每个子帧的令牌变化打印出来看着它从正数变成负数、再被PBR慢慢补回来的过程比读十遍标准文本都管用。