
凌晨两点被电话叫起来说某个 CDN 边缘节点上单个客户的回源带宽跑到了机柜出口上限同机器上其他几个域名跟着一起抖。登上机器一看CPU 利用率不到 15%网卡队列也没什么积压可业务侧测速就是上不去。最后翻到那块 SA 板卡的硬件计数器才发现板上挂着的限速策略早就生效了而且限得比预期狠一大截——突发桶配得太小。这件事之后我才把 CDN 场景下 SA 板卡限速这套东西从头到尾捋了一遍也踩了不少本来可以避开的坑。这篇东西想聊的就是这个在 CDN 节点里SA 板卡上的限速到底是个什么机制它和我们在操作系统里用 tc 做的那套限速有什么本质区别参数该怎么算配下去之后怎么验证以及实测中最容易翻车的那几个点。适合已经摸过交换机 QoS、做过边缘节点运维、或者正在给自建 CDN 选加速板卡的同行看如果你只是听说过限速两个字但没动过手前面几节的基础拆解也能让你跟上。1. CDN 边缘节点里的 SA 板卡到底在替谁干活1.1 先搞清楚 SA 板卡在节点里的位置SA 板卡这个叫法在不同团队里含义不太一样有人叫它业务加速卡有人叫智能网卡还有的直接按芯片类型叫。落到 CDN 边缘节点这个具体场景它通常是一块插在服务器 PCIe 槽上的加速卡形态上可能是基于 FPGA 的可编程卡也可能是带独立网络处理单元的 SoC 卡对外提供一到两个高速以太网口对内通过 PCIe 和主机通信。它的核心价值是把一批原本要 CPU 逐包处理的事情搬到板上做报文解析、连接跟踪、加解密、转发查表、流量统计当然也包括这篇要讲的限速整形。为什么 CDN 节点特别需要这么一块卡因为 CDN 边缘服务器的流量模型和普通业务服务器完全不是一个量级。一台普通 Web 服务器跑满千兆就算不错了而一台边缘缓存节点动辄就是双万兆甚至双 25G 起步小包占比还高一个 1KB 以下的响应对象能占到总请求数的六七成。这种场景下如果每个包都要进内核协议栈走一遍 netfilter、再走一遍 qdiscCPU 光在软中断上就能吃掉好几个核。板卡把这一层接管过去主机侧看到的就是一块已经帮你分好流、记好账、限好速的网卡。需要说明的是不同厂商的 SA 板卡在卸载边界上差别很大。有的只做纯转发和统计限速还是回主机做有的把整形和限速完全下沉到硬件流水线主机侧只能下发策略、读计数器。这两种形态在运维体感上完全是两回事选型的时候一定要问清楚限速是在哪一层生效的这个后面会展开讲。1.2 从一次带宽跑飞的故障看限速的必要性回到开头那次故障。出问题的客户是个视频点播业务平时峰值也就几百兆那天上了个新专题页短时间内大量用户同时拉同一批冷门分片回源请求把源站打满了。节点侧的表现是出口带宽被打满同机其他域名开始出现首包延迟抖动。这时候如果节点上没有任何限速手段结果就是一个客户拖垮一整台机器。CDN 是多租户环境一台边缘节点上往往跑着几十上百个域名共享同一组出口带宽。没有隔离就没有稳定性可言。限速在这里承担的角色本质上是把共享资源切成有边界的配额让任何一个租户的突发流量都超不出自己的那一份。但限速又不只是防跑飞这么简单。它同时还承担着几个隐含职责给上游链路做整形以避免中间设备被动丢包、给回源链路做保护以避免源站被打穿、给不同计费档位的客户做差异化服务。这几件事对限速的精度、粒度、响应速度要求完全不同所以在 SA 板卡上配限速之前先得想清楚这一条策略到底是为哪个目标服务的不然参数根本没法定。2. 限速为什么被搬到板卡上软件限速的三笔隐形成本2.1 CPU 软限速在万兆链路上的失效点主机侧做限速最常用的就是内核的流量控制子系统配个层次化令牌桶或者简单的令牌桶过滤器挂到出口网卡的根队列上。这套东西在千兆环境下非常好用配置灵活、观测方便、生态成熟。但到了双万兆、25G 这个量级问题就开始密集出现。第一笔成本是中断与调度开销。限速本质上是对每个包做一次是否放行的判定这个判定本身不复杂但它是逐包发生的。25G 线速下小包能做到每秒接近三千万个就算每个包只花几十纳秒做判定累计起来也是相当可观的 CPU 时间更别说这些判定还要和软中断、NAPI 轮询抢同一个核。第二笔成本是时间戳精度。令牌桶的准确性高度依赖现在是什么时刻这个判断。软件限速拿到的时钟精度受调度延迟影响在高负载下抖动可能到几十微秒甚至更高。抖动一大令牌补充就不均匀表现出来就是明明配了 1Gbps实测却在 800Mbps 到 1.1Gbps 之间来回跳。业务侧看到的就是速度不稳。第三笔成本是多级队列的内存与锁竞争。要给上百个租户各配一条限速就得有上百个队列实例每个实例带自己的令牌桶状态、统计计数、锁。队列一多缓存局部性变差锁竞争也会冒出来。我见过一台机器上挂了三百多条限速规则之后光是队列管理本身就把一个核吃满了。2.2 板卡级限速的精度来源SA 板卡把这些判定搬到了硬件流水线上带来的第一个直接好处就是时钟确定性。板卡上通常有独立的硬件计时单元精度可以做到纳秒级而且不受主机侧调度影响。这意味着令牌补充的节奏是严格均匀的长期平均速率能稳稳压在配置值上偏差通常在千分之几以内。第二个好处是并行处理。硬件流水线天然支持多队列并行几百条限速规则同时生效也不会互相拖累。每条规则在流水线里对应一个独立的上下文判定和更新是流水线的一个阶段和主机的负载完全解耦。第三个好处是计数可信。板卡上的字节计数是在报文进入流水线的瞬间打的不经过内核协议栈也不会因为缓冲区拷贝而重复计数或者漏计。这一点在做租户带宽结算的时候特别重要——用主机侧统计和用板卡统计两边的数字经常对不上对账的时候非常难受。代价当然也有。硬件限速的灵活性远不如软件很多板卡只支持固定几种算法参数范围也有限制改一条策略往往要重新下发整张表,下发过程可能带来短暂的行为不一致。这个后面在踩坑章节会具体讲。对比维度主机侧软件限速SA 板卡硬件限速时钟精度受调度影响微秒级抖动硬件计时纳秒级逐包开销占用 CPU小包场景压力大流水线处理与主机负载解耦规则规模数百条后管理成本明显上升数千条量级仍可线速参数灵活性算法与参数高度可调受硬件能力约束选项有限统计可信度受协议栈路径影响报文入口处计数偏差小策略变更热更新影响面小可能整表下发存在瞬态3. SA 板卡限速的实现模型与参数怎么算3.1 令牌桶、漏桶与三色标记的取舍板卡上实现限速主流就那么几套模型。最基础的是单速令牌桶以一个固定速率往桶里放令牌桶有固定容量包来了要消耗等于自己长度的令牌不够就排队或者丢弃。这套模型只有两个参数——速率和桶深配置简单但缺点是不支持突发速率和承诺速率分离。再往上是双速令牌桶也就是承诺速率和峰值速率分开各配一个桶。这套模型适合那种平时按承诺带宽走允许短时冲到峰值的业务CDN 里做分级计费经常用。参数多了两个但语义清晰。用得最多的是三色标记这一族包括单速三色和双速三色。它的思路不是简单地放行或丢弃而是把包分成绿、黄、红三类绿色直接放行黄色在拥塞时优先丢弃红色直接丢。这样在链路不拥塞的时候可以容忍超额流量拥塞时才收紧。对 CDN 这种平时希望尽量跑满、峰值时必须守住底线的场景三色标记的适配度明显更高。选型的时候有个经验如果这条限速是为了计费隔离用双速令牌桶边界清晰好对账如果是为了抗突发保护上游用双速三色让超额流量在拥塞时被优先牺牲而不是一刀切如果只是简单地防止单租户跑飞单速令牌桶配个合理的桶深就够了别把模型搞复杂。3.2 CIR、CBS、PIR、PBS 这四个参数怎么定这四个缩写是限速配置里绕不开的承诺信息速率、承诺突发尺寸、峰值信息速率、峰值突发尺寸。前两个是保证给你多少后两个是最多允许你冲到多少。真正难的不是理解定义是把数字算出来。先说承诺速率。这个不能拍脑袋得看这个租户的历史带宽分位值。我的做法是拉最近 30 天的 95 分位带宽取它的 1.1 到 1.2 倍作为承诺速率。取 95 分位而不是峰值是因为峰值往往只出现在极短的窗口里按峰值配会让整体配额严重超卖乘个系数是为了留出业务自然增长的余量不用天天调。再说承诺突发尺寸。这个参数最容易被随手填个大数但它其实决定了允许多长时间的超额。桶深的物理含义是在承诺速率下桶被灌满需要多长时间。经验值是把这个时间控制在 10 到 50 毫秒之间。为什么是这个量级因为 TCP 的拥塞窗口增长是以往返时延为周期的桶深覆盖的时间如果明显小于一个往返时延TCP 就会频繁撞到限速边界吞吐上不去如果远大于往返时延突发就放得太松起不到保护上游的作用。按 20 毫秒算1Gbps 的承诺速率对应桶深大约是 2.5MB这个数字可以直接拿来当起点。峰值速率一般配成承诺速率的 1.5 到 2 倍具体看上游链路的余量。峰值突发尺寸通常取承诺桶深的 2 到 4 倍让短时突发有地方缓冲。举个具体例子某租户 95 分位带宽 800Mbps上游出口还有 40% 余量。承诺速率取 900Mbps峰值速率取 1.6Gbps承诺桶深按 20ms 算是 2.25MB峰值桶深取 6MB。这组数字配下去之后实测长期均值稳定在 890 到 910Mbps短时突发能顶到 1.5Gbps 左右符合预期。3.3 限速作用点选在端口、队列还是业务流同一个限速需求作用点不同效果差别很大。板卡上通常有三档可选。端口级是整块板卡的一个物理口做整体限速最简单但粒度最粗只能防整机跑飞做不到租户隔离。队列级是把流量先分类到不同队列每个队列独立限速这是 CDN 里最常用的因为可以按域名、按租户、按业务类型分队列。流级是精确到五元组粒度最细但规则表规模会爆炸一般只用在需要精确控制的少数场景。选队列级的时候有个关键细节分类规则要尽可能前置。理想情况下报文一进板卡就根据目的 IP 或者 VLAN 标签完成分类后面所有处理都在确定的队列上下文里做。如果分类依赖深层字段解析比如要看 HTTP 头或者 TLS 里的信息那就得先做重组或者深度解析延迟和资源开销都会上去。我的习惯是尽量用 IP 段或者 VLAN 做分类依据把租户和 IP 段的映射关系在上层维护好板卡侧只做简单的查表。4. 把限速配上板卡一次完整的落地流程4.1 上线前的链路画像与基线测量配限速之前必须先有一份基线否则后面根本没法判断效果。基线要测三样东西空载下的最大吞吐、典型业务流下的包长分布、当前的实际带宽曲线。最大吞吐用打流工具跑一下就行注意要双向都测而且要分别测大包和小包。小包吞吐往往才是瓶颈所在25G 口跑 64 字节小包实际能到的包速率可能只有理论值的六七成。包长分布可以从现网采样也可以从历史统计里拉重点看平均包长和小于 128 字节的占比这个数字直接影响后面桶深该按字节算还是按包算。带宽曲线至少要拉一周的按五分钟粒度看。重点观察两件事有没有周期性的尖峰以及尖峰的持续时间和幅度。有些业务的尖峰是每天固定时间出现的备份流量这种可以用时间维度的策略切换来处理不必把常态配额配得很大。这一步还容易被忽略的是上游设备的实际能力。限速的目的是保护上游但如果上游本身的缓冲就很浅那把突发放得再宽松也没意义。有条件的话看一下上游接口的队列丢弃计数如果平时就有零星丢弃说明上游本来就紧张限速应该收紧一点桶深也要压低。4.2 策略下发与硬件计数器核对策略下发这一步不同板卡的管理接口差别很大有的是命令行有的是配置接口有的是通过驱动提供的工具。不管哪种下发之后第一件事是把策略读回来核对确认板卡上实际生效的参数和你算出来的数字一致。我遇到过不止一次参数被静默截断的情况——配置里写的是 2.5MB 桶深板卡实际只接受了 1MB因为内部字段位宽不够超出部分直接被截掉还不报错。核对完参数接着要确认计数器已经开始工作。板卡上一般会有几个关键计数通过字节数、丢弃字节数、整形队列深度、当前令牌余量。策略刚下发、流量还没上来的时候通过字节数应该是缓慢增长的丢弃计数应该为 0。如果一上来丢弃计数就蹭蹭涨多半是桶深配太小或者承诺速率配太低。这里有个实用技巧把整形队列深度也纳入监控。队列深度持续非零说明流量长期贴着限速上限跑整形在起作用队列深度忽高忽低但均值很低说明只是偶发突发属于正常队列深度一直是零但丢弃计数在涨那就说明根本没走整形是直接丢弃这种情况要么是板卡工作在监管模式而不是整形模式要么是队列配置有问题。4.3 压测验证与调参收敛参数下发完一定要做一轮定向压测。压测不是简单地把带宽打满看丢不丢包而是要有针对性地验证几个行为。第一轮验证长期均值用固定速率持续打十分钟以上看板卡统计的实际通过速率是否收敛到承诺速率。偏差控制在 3% 以内算正常超过 5% 就要查参数。第二轮验证突发容忍度用短时高速率脉冲打脉冲时长设在桶深对应的时间附近看多少个脉冲之后开始出现丢弃。这一步能验证桶深是否真的按预期在工作。第三轮验证多流并发同时打几十条流模拟真实的多租户场景看各队列之间是否互相影响。这一步最考验板卡的多队列调度能力有些板卡在队列数少的时候表现很好队列一多就开始出现互相干扰。调参的方向也要说清楚均值偏低就调高承诺速率或者加大桶深突发容忍不够就加大桶深上游被打爆就压低峰值速率。每次只调一个参数调完重新跑一轮不要一次改好几个不然出了问题根本不知道是哪个参数导致的。5. 实测中最容易翻车的几个限速场景5.1 突发桶配小了TCP 吞吐直接腰斩这是最常见的坑也是我开头那次故障的直接原因。表现很有迷惑性明明限速配的是 1Gbps业务侧测速只有 400 到 500Mbps而且抓包看丢包率并不高就是速度上不去。根因在于 TCP 的发送行为和桶深的关系。TCP 建立连接后会经历慢启动拥塞窗口指数增长每一轮往返就把窗口翻倍短时间内会集中发出一大批报文。如果桶深只够装下这个突发的一小部分超出的部分就会被整形排队甚至丢弃。丢包一出现TCP 立刻把窗口砍半进入拥塞避免增长速度骤降。如此反复连接就永远停在冲一下、被打回、慢慢爬的循环里平均吞吐远低于限速值。判断方法很简单看板卡的整形队列深度曲线如果在每个往返周期附近出现明显的锯齿那就是典型的桶深不足。处理办法是把桶深按往返时延的 1.5 到 2 倍来配比如实测往返时延 30 毫秒桶深就要覆盖 45 到 60 毫秒的量。这个数值比前面说的 20 毫秒经验值要大说明经验值得结合实测算出来的往返时延一起看不能生搬。还有一个细节容易被忽略整形模式和监管模式的行为完全不同。整形是把超出的包排队等一下再发监管是直接丢。前者对 TCP 友好得多后者在高突发场景下很容易把连接打进拥塞避免。配策略的时候一定要确认工作在整形模式尤其是有多个队列的时候某些板卡默认只有根队列做整形子队列是监管这个默认值不改就会踩坑。5.2 多级限速叠加后的幽灵丢包第二个坑更隐蔽。节点上通常不止一级限速板卡上有队列级限速交换机端口上可能还有一层上游链路可能还有一层。这三层如果都配了实际生效的是最紧的那一层但问题是——你往往不知道哪一层在丢包。我遇到过一次排查了三天的案例某租户反馈下载速度上不去板卡计数器显示丢弃为零交换机端口计数显示有少量丢弃但量级对不上业务侧感知的差距。最后发现是交换机上有一条历史遗留的端口限速速率配得比板卡上的还低而且桶深极小。流量在板卡上顺利通过到交换机上被打散表现就是板卡说没事业务说很慢。排查这类问题的思路是从下游往上游逐级对齐计数。先看业务侧感知的速率再看板卡通过速率再看交换机端口速率每一级都要比。如果某一级的通过速率明显低于上一级问题就在那一级。同时要看各级的丢弃计数和时间戳丢弃是不是发生在同一批时间点上这个能快速定位到责任层。经验上多级限速叠加的时候越靠近业务的层应该配得越宽松越靠近上游的层配得越严格形成梯度避免出现下游放行、上游打回的浪费。而且每加一层都要在文档里记清楚不然半年后没人说得清这台机器上到底有几层限速在生效。5.3 硬件计数与业务侧统计对不上第三个坑出现在对账环节。板卡统计的通过字节数和业务侧自己记的下载量经常有 5% 到 15% 的差距做带宽结算的时候双方各执一词。差距的来源主要有几个。一是计数层次不同板卡统计的是链路层字节包含以太网头和前导码业务侧统计的是应用层有效载荷中间差了 IP 头、TCP 头、TLS 记录头这些开销。这个差距在包长较小的时候尤其明显64 字节的包有效载荷可能只有十几个字节两层统计能差出好几倍。二是重传流量板卡看到的是实际发出的所有字节包括 TCP 重传的部分业务侧通常只统计成功送达的量。限速配得紧、丢包多的时候重传占比会上升两边差距就拉大。三是计数时机有的板卡在报文入队时计数有的在出队时计数入队计数会把后来被丢弃的包也算进去。这个一定要跟厂商确认清楚不然数字根本没法用。处理办法是在结算口径上先对齐明确规定按哪一层的字节计数然后用固定的修正系数去换算。更稳妥的做法是让板卡同时输出包数和字节数用平均包长做交叉验证如果平均包长在合理区间内说明计数是自洽的。6. 限速策略的长期维护与容量视角6.1 策略不是配完就结束限速策略上线只是开始。业务在变租户在增减流量模型在漂移配好的参数过几个月就不合适了。我的做法是给每条策略配三个监控指标实际通过速率相对承诺速率的占比、整形队列深度的分位值、丢弃字节数的日环比。占比长期高于 90%说明配额快不够了要考虑扩容或者重新谈档位长期低于 50%说明配额浪费可以回收一部分给别的租户。队列深度 95 分位持续偏高说明突发经常打满缓冲区桶深可能要调大。丢弃字节数突然跳变一般意味着业务侧有异常或者上游链路出了变化需要立刻查。还有一个容易忘的点策略的失效时间。临时配的限速一定要带过期时间不然过几个月谁都不敢删。我见过一台机器上挂着十几条两年前临时加的限速早就不对应任何真实业务但没人敢动因为不知道删了会不会出事。后来是逐个做流量观测确认无流量之后才清掉的。6.2 从限速数据反推节点容量限速数据用好了其实是个很好的容量规划输入。把所有租户的承诺速率加总和节点实际出口带宽比一下得到的就是超卖比。CDN 场景下超卖是常态因为所有租户不会同时跑满但超卖比要有个度。经验上超卖比控制在 3 到 5 倍之间比较安全超过 6 倍之后一旦遇到全网级别的热点事件所有租户同时冲高节点必然顶不住。另一个指标是峰值速率加总。把所有租户的峰值速率加起来如果已经超过了出口带宽的 1.5 倍说明理论上存在被打爆的可能虽然实际发生的概率不高但一旦发生就是整机级别的故障。这种情况要么扩容出口要么把峰值速率整体往下压一档。我自己维护节点的时候会把这两个比值做成周报看配合实际的带宽使用率曲线一起看。带宽使用率的 95 分位如果连续几周往上走加上超卖比也偏高那就是明确的扩容信号不用等到真的出故障才动手。最后分享一个实操小技巧调限速参数的时候先在测试节点上跑别直接上生产。测试节点不用配得和生产的规模一样只要板卡型号、驱动版本、业务流量模型接近就行。我习惯在测试节点上用固定脚本跑一轮标准压测把结果存成基线以后每次改参数都对比着看参数变化带来的影响一目了然比凭感觉调靠谱得多。