
做scale-up互连做了几年如果用一句话总结我踩过的坑那就是光路一抖协议栈白走。这里说的scale-up指的是把高速计算节点、存储节点和交换矩阵纵向扩展成一个超大逻辑系统的互连协议族它和传统横向偏重的尽力而为网络完全是两个物种——强一致、低时延、带宽密集。而光链路恰巧是这套体系里最难伺候的一环。电信号在PCB上走几英寸都得精心阻抗匹配光信号却要经历电光调制、光纤传输、光电转换三个环节任何一个环节出现劣化最终都会猪八戒照镜子——里外不是人变成主机侧看到的链路震荡、误码爬升、重训练风暴。所以今天这篇文章我想把我某互连协议项目中针对光链路可靠性做的一整套设计完整拆开光模块健康监测、FEC纠错、链路层重传、链路训练、故障切换。每一层都不是拍脑袋加的而是把实际故障按“坏了会怎样、什么时候能发现、多久能恢复”逐个过一遍之后才定下来的。总量不短建议收藏后慢慢看。1. 光链路在scale-up场景下为什么“不敢坏”1.1 scale-up协议对链路质量的苛刻要求先理解一个背景scale-up协议的设计初衷是把几十个甚至上百个高速节点“捏”成一个整体。这种场景下链路不只是传数据它还在承载一致性状态、同步栅栏和全局调度消息。以典型的RDMA风格语义为例发送端一旦把数据放到线上接收端要么完整拿到要么协议栈就要触发整条流的中断和重排。普通以太网里丢一个包可能只是应用感知到一点延迟但在scale-up环境里丢一个包可能导致整个跨节点任务进入等待、锁死甚至连锁触发一次全局flush。这就决定了我们对光链路的容忍度极低。电域设计可以通过预留眼图余量、阻抗补偿来抹平问题光链路却要额外面对光路污染、激光器老化、光纤微弯、连接器端面损伤这些电域根本不存在的物理变量。更麻烦的是这些变量往往是有记忆效应的今天看到的误码率可能还是1E-12三天后就跳到1E-6没有任何预兆。你没法靠“坏了再修”的思路因为等它彻底“坏了”整网已经被冲击过一遍了。1.2 光链路失效的常见形态与根因我把日常运维和调测中遇到的光链路故障整理成了下表后续所有可靠性设计都是围绕这几类失效展开的失效形态现象特征常见根因故障发展速度瞬断信号完全丢失重训练连接器松动、线缆位移、端面脏污毫秒级快速劣化误码率飙高FEC告警激光器老化、驱动芯片性能退化分钟至小时级缓慢劣化误码率渐进升高时好时坏光纤弯曲损耗、端面污染积累天至月级间歇性抖动偶发错误、CRC间歇告警SerDes失配、DFE抽头收敛异常不确定注意看第二类和第三类这是最阴间的两类。瞬断至少能被快速感知设备马上进入链路训练慢劣化才是真正的挑战——在工作负载低的时候错误间隔足够长FEC能全兜住指标一切正常。一旦业务高峰流量拉满错误突发密集到FEC纠不过来协议层就会毫无防备地暴露在误码之下。这个特征直接引导我把可靠性设计拆成了两级一级负责“提前发现”一级负责“错误兜底”。只要发现够早兜底够厚系统就不会走到断链那一步。2. 可靠性设计的总纲分层设防、闭环自愈2.1 设计目标先定指标再谈方案做工程最怕没有量化目标就开始堆功能。我们做这套设计时先花了一周时间把指标定死后面所有选型都拿指标检验。核心指标定三个目标可用性单条光链路年可用性不低于99.999%折算成年故障时间约5.3分钟这已经是很激进的目标故障感知时间从物理层劣化到系统感知并介入目标小于50毫秒无感切换能力因链路质量问题触发的主备切换或重训练应做到业务流感知不到即应用层链路恢复时间小于200毫秒。这三个指标直接决定了设计取舍。99.999%可用性意味着你不能等链路彻底失效再动作必须做趋势预测50毫秒感知意味着轮询周期不能太长硬件事件上报优先级必须高于软件巡检200毫秒恢复意味着协议栈的握手和状态重建必须极简不能在重训练上浪费大段超时等待。2.2 分层防护架构光链路可靠性不能只靠某一层的“神医”必须分层设防。我最终落地的防护架构是四层每层解决一类问题层级职责核心机制响应速度物理监测层发现劣化趋势DOM光功率/温度/偏置电流监测、阈值告警秒级链路纠错层吸收随机离散错误RS类FEC解码、纠错计数统计实时传输恢复层处理FEC无法吸收的错误突发CRC校验、超时重传、流控暂停微秒至毫秒级系统切换层在不可恢复故障下保全业务主备链路切换、快速重训练、流量重路由十毫秒至百毫秒级这四层之间不是简单的上下级关系而是“前面顶不住就交给后面”的递进关系。FEC把大部分随机错误吃掉万一出现连续突发错误传输层必须能感知并重传如果链路已经物理失联系统层就要果断把流量切换到备用路径上。每一层都要假设下一层不存在按照“只有我能兜住”的标准去设计最终可靠性才能叠起来。这里有个很重要的经验越靠下层越要主动越靠上层越要克制。物理层监测宁可误报也不能漏报误报最多导致运维多查一次传输层重传不能太激进否则会引发拥塞控制抖动系统层切换更要保守频繁切换会把正常工作的链路也搞得鸡飞狗跳。可靠性的核心不是“每一个机制都做到最强”而是“每个故障都有且只有一个机制负责兜住”。3. 第一道防线光模块健康监测与劣化预测3.1 可监测的物理量有哪几个光模块数字诊断监测DOMDigital Optical Monitoring几乎已经是可插拔光模块的标配能力。模块内部会监控四个核心物理量经标准总线读取并向管理系统上报。这四个量分别是模块温度反映模块工作环境热应力SerDes芯片和激光器驱动对温度都很敏感供电电压电压漂移直接反映模块内部电源质量异常波动往往是钽电容老化或电源纹波变大的先兆激光器偏置电流这是激光器老化最直接的指纹随着发光效率下降驱动电路会自动调高偏置电流来维持恒定光功率电流爬升速度能侧面反映激光器寿命发射/接收光功率发射光功率反映激光器出光状态接收光功率则直接反映整个光通道的插损变化。这四个量单独看都很单调但组合起来能派大用场。举个例子如果接收光功率缓缓下降、但发射光功率纹丝不动大概率是光纤链路本身的插损在变大常见原因是连接器端面污染或光纤被过度弯折。如果发射光功率没变、偏置电流却在涨说明激光器在悄悄老化。如果两者都正常、只有温度在爬升那问题可能出在机柜散热而不是光路本身。3.2 阈值设计绝对门限与趋势门限只监测不设阈值等于没监测。阈值设计我建议做成“绝对门限趋势门限”双轨制。绝对门限解决的是“现在是否已经不安全”的问题一般是厂商给出的极限值打折使用。以典型多模光模块为例接收光功率的告警阈值通常在-12dBm左右但我会把预警阈值设在-10dBm留出2dB余量。不是说-10dBm就一定不安全而是这个点意味着链路预算已经消耗了很大一块后续一个插损抖动就可能直接冲垮它。同样激光器偏置电流超过初始化值的15%我会标记为“轻度老化”超过25%则直接进入“限期更换”队列。趋势门限解决的是“未来是否要出问题”的问题。光功率漂移、偏置电流爬升、温度波动都不是突变而是有趋势的。我在设备上做的是每小时记录一次快照计算24小时和7天的滑动平均值并拟合漂移速率。规则很简单如果接收光功率的24小时漂移超过1dB或者偏置电流7天爬升超过5%就判定该链路存在劣化趋势触发人工巡检。这套规则落地后我们成功在两次真正的连接器污染故障发生前一周捕捉到了异常信号运维同事有充裕时间安排窗口清洁。3.3 从“报警”到“故障前预警”绝对门限是故障发生时的“报警”趋势门限是故障发生前的“预警”两者价值完全不同。报警大多出现在业务受损之后你的响应是被动的预警则给了你一个从“故障响应”切换为“计划维护”的窗口。实际落地时我建议把监测数据接入统一的健康度评分90分以上正常70到90分观察50到70分预警50分以下限期整改。评分算法不必花哨把温度、光功率、偏置电流的劣化程度映射成一个严格单调递减的得分即可。关键是评分结果要和运维工单系统联动一旦进入预警区间自动创建维护任务并限制该链路承载高优先级的跨节点同步流量。还有一个细节很容易被忽略模块信息读取是I2C总线轮询频率不能太高否则模块内MCU会被频繁访问拖垮。我的经验是常规状态每10秒轮询一次就够了但这指的是“顺带扫描版本号、厂商信息这些不变项”。真正需要拉取的光功率、电压、偏置电流三个变量至少要保证每秒钟刷新一次才能捕捉到故障发生前的快速变化同时又不至于把模块I2C阻塞到影响正常通信。4. 第二道防线FEC纠错和误码率管理4.1 FEC为什么必须放在PHY和链路层之间光链路的噪声不可能完全消除所以纠错不是可选项而是必须项。FEC前向纠错的设计逻辑非常朴素发送端为每段数据附加一段校验码接收端收到后利用校验码自动纠正一定数量的错误比特不需要重传。它适合处理光纤传输中的随机噪声、散粒噪声、模态噪声导致的离散误码。FEC的摆放位置很讲究。放在物理层内部能做到最实时、最低时延但不能感知协议语义放在链路层之上又会让纠错延迟变大。我在这个项目里采用的是物理编码子层内部集成FEC也就是在SerDes和协议MAC之间加一个编解码层。这样FEC对协议栈几乎透明协议侧看到的是一个几乎无错的通道而FEC解码器自身的纠错统计则作为链路质量的核心观测面。4.2 纠错能力与误码率目标FEC的纠错能力是“过增”的加的保护越多纠错越强但有效带宽和时延越差。以典型的RS(544,514)码为例它每个码字携带514字节净荷附加30字节校验能纠正最多8个错误符号。这个选择是平衡后的结果——再强的纠错比如RS(544,492)能扛的错误更多但净荷占比低带宽损失将近10%放在带宽饥渴的scale-up场景里非常不划算。既然是scale-up必然要关心性能边界。FEC生效时的最大可用纠错前误码率也就是BER大约在1E-4级别。换句话说只要物理层误码率不超过1E-4FEC都能全部纠回来业务侧看到的是零误码一旦超过这个量级FEC开始“纠不过来”部分错误码字会被标记为不可纠正直接扔给上层。这里要引出三个容易被搞混的概念纠错前BER、纠错后BER、不可纠正码字率。纠错前BER反映的是物理信道的“原生”质量这是光模块和光纤关心的事纠错后BER反映的是FEC之后真正进入协议栈的可靠性这才是对上层有意义的数据不可纠正码字率则是故障跳水前的最后信号。我强烈建议监测系统把这三个指标全部采集并分别设阈值。纠错前BER接近1E-4时建议预警纠错后BER高于1E-12时视为异常不可纠正码字数从0跳到一个非零值说明链路已经进入“癌变期”要立即启动重传或切换流程。4.3 重传策略FEC纠不动的时候怎么办FEC永远不可能把所有错误都吞掉所以必须有后手。在链路层我会给业务流增加一个轻量级确认机制——接收端在固定时间窗内没有发现错误就不做响应一旦FEC解码器连续出现不可纠正错误接收端在MAC层生成NACK要求发送端重传对应的数据块。重传策略上最容易犯的错是把重传粒度设置过大。一开始我们的重传单位是整个消息结果一个突发错误导致整条消息的几个MB重复推送链路被无意义数据撑爆。后来改成按64KB块重传加上滑动窗口限制负载显著下降也让无错误的重传覆盖范围更精准。另一个值得注意的点是重传触发的退避当不可纠正错误频繁出现时如果每次都立即重传会形成“错误—重传—再错误—再重传”的死循环。退避机制不能像以太网那样二进指数退避那样可能退避到几十秒scale-up协议根本等不起。我的方案是只退避到固定档位比如重传间隔从1微秒涨到10微秒不再继续放大保证链路还有机会快速恢复。把重传做在前面之后误码对应用的影响被大大压低。实测数据里在光纤端面轻度污染、纠错前误码约2E-5的条件下FEC加重传可以把应用可见的丢包率从3%压到万分之五以下这个结果让我觉得这两层配合才算真正值回票价。5. 第三道防线链路训练、故障切换与自愈5.1 链路训练机制让链路“握手”得更聪明光链路有一个不亚于误码的敌人——链路建立和恢复时的训练过程。协议规定链路从待机到工作状态要经过发送端和接收端的参数收敛包括信号幅度、均衡器系数、时钟恢复等。这一过程如果设计得僵硬就会出现“一坏就断、一断就要训练、训练又要很久”的恶性循环。我做链路训练设计时关键动作是三段式先低速握手再快速预收敛最后切高速运行。低速握手阶段交换链路参数确认双方能力集花的时间极短预收敛阶段使用固定的、保守的均衡参数启动传输保证在光纤劣化条件下也能锁定时钟稳定运行之后再渐进式提高均衡增益跟踪光通道的实际特性。这套机制最大的收益是即使链路经历过一次瞬断恢复时也不会从零开始“盲人摸象”。我曾对一个拔纤场景做过压力测试旧方案每秒拔一次彻底恢复平均要400毫秒新方案能做到150毫秒左右效果好到肉眼可见。还有一个值得注意的细节训练期间的错误处理要宽容。训练阶段本身就是参数探索过程偶尔出现误码是正常的不该立刻触发重训。我在实现里加了“训练抖动容忍窗口”——只要错误间隔大于某个阈值就继续当前训练状态只有错误密集到窗口被塞满才中止并重新开始。这个设计看起来简单却避免了很多次“参数即将收敛、却因为抖动被强制打断”的假性失败。5.2 故障切换设计不能让单点拖垮全局再可靠的光链路也有拉不回来的时候例如光纤被物理切断、模块彻底烧毁。这时候唯一能做的就是快速把流量切到备用路径。我在scale-up组网里采用的是11主备链路设计而不是全网动态路由。11有个巨大的好处——切换决策在本地即可完成不需要全局路由收敛。每一条业务流在主状态机里登记主链路和备用链路各自的健康状态当主链路健康度跌到切换门限以下流量即时切入备用链路。切换最关键的是“对端知晓”。不能只是发送端单方面换了路径接收端还固守在旧链路上等数据。我设计的方案是同步切换协议发送端在切换瞬间在备用链路上发送一个带有切换序列号的切换通告帧接收端收到后更新自己的接收状态机并对旧链路上的未完成数据保持一段时间的容忍接收。这个通告帧只有十几个字节却解决了“双端状态不一致”这个最容易引发切后乱序的根源。主备切换的决策也不能只看瞬时状态。我设定了“两次确认”规则连续确认主链路在两个评估周期内都跌入劣化区才真正执行切换。这是为了防止偶发毛刺导致误切换——偶发毛刺可能只是光纤被碰了一下系统却为此付出流量短暂中断的代价得不偿失。这个“保守切换”的思路很反直觉可实测下来非常值得误切换引起的损失远大于晚几十毫秒切换的损失。5.3 快速重训练与流量保护如果主备链路同时出问题或者备用链路来不及建立快速重训练就是最后的救火队员。快速重训练不能走完整的参数协商流程而应直接使用上次成功训练时保存的收发参数重启链路。我在项目里定义了一个“链路参数快照区”每次成功训练完成后把当时的均衡系数、幅度档位、时钟恢复配置全部写入非易失存储。故障时直接调取快照启动训练流程从几十步缩短为三四步速度能比完整重训快上3到5倍。快照也不是永久有效。链路参数会随着光模块的老化和温度漂移发生变化三个月前的快照可能已经不准。因此我加了一条规则快照保存超过24小时或光电模块更换过就强制走一遍完整训练流程重新生成快照。很多工程师之所以在这类问题上栽跟头就是因为在硬件更换后仍机械地使用陈旧快照结果重训成功率非常感人。快速重训练完成后发送端的流量要“低水位起步”。我采用的做法是先在备用链路上发送管理帧验证链路质量等确认FEC解码器返回的纠错前BER低于1E-6再逐步放开业务流量到满负荷。这避免了很多重训后仓促放量导致二次震荡的情况出现。6. 实测、调优与常见坑6.1 造故障的方式不真实打坏怎么验证可靠性可靠性设计最忌讳“纸面论证”。我在项目里建立了一套造故障环境专门用来验证上述设计是否真的可靠。光链路的故障注入套路不外乎这几种拔纤实验考验瞬断检测和快速重训最简单也最伤头发可变衰减器在光路上串入可调衰减器通过增加衰减模拟慢劣化用来验证趋势监测是否在故障前报警光功率抖动发生器让光功率快速波动模拟连接器松动场景测试切换机制会不会被毛刺忽悠温度冲击对模块局部加热或快速降温模拟热应力带来的参数漂移主要验证VCSEL激光器在温度剧变下的表现。衰减实验里有一个细节衰减从小到大逐步增加每隔两档停留30分钟观察FEC纠错统计是否线性增长。如果纠错次数突然出现量级跳变说明链路不是“均匀劣化”而是进入了某种不稳定状态这往往是连接器端面损伤而非单纯的损耗变大。6.2 典型翻车案例复盘案例一FEC工作正常但重传风暴不止。有一次压测时我们发现FEC纠错统计并不高但协议栈却回放极多的NACK排查下来发现是偶发不可纠正码字数突然上扬导致重传机制里“触发重传的阈值”设置得太低。FEC纠错次数是稳定的但只要出现一个过载码字就足以让接收端半层拒绝一批数据。后来我们将NACK触发从“只看到单个不可纠正码字”调整为“一个评估窗口内连续出现多个码字”重传率立刻恢复平静。案例二模块状态正常但误码率持续爬升。这是最迷惑人的一种故障DOM上报的四种物理量全部绿灯FEC却在持续报警。排查到最后发现是光纤跳线的弯曲半径太小弯到临界点但并未到达阈值物理量层面看不出任何问题。这提醒我们DOM监测覆盖的是模块自身和部分链路信息但光纤走线、跳线盘绕这些“光路体质”问题靠模块自身指标根本兜不住。所以必须在机柜侧做好光纤走线规范跳线盘绕半径不能小于厂商建议值连插损仪测出来正常也不能掉以轻心。案例三链路训练反复失败。我们把训练失败率从“偶尔一次”折腾到“十次八败”之后才意识到问题出在训练参数协商里的接收端均衡档位设置上而不是光路质量。接收端的自适应均衡器在低收益参数下收敛太慢导致训练阶段动不动就超时。后来改成从高增益档位逐步下调而不是从默认档位上攀收敛时间直接砍半。这条经验很重要参数探索方向也要跟着光路的物理特性走不是随便选个方向就能优化出最优解。6.3 避坑清单与经验心得别为了所谓“高可靠性”无限增强FEC强度带宽损失会让设计者痛不欲生任何纠错能力都要用“典型故障场景下的业务收益”来验证趋势监测的采样间隔不能太长24小时只采一次和没采一样至少要能捕捉到小时级漂移切换决策要“手稳”宁可慢一两个周期也不要被毛刺带偏节奏稳定压倒一切训练快照要带上时间戳和模块序列号任何一次硬件变更都要视为快照失效所有的阈值参数都不能拍脑袋一定基于对端口的长期观测数据来定初期可以留宽一点观测数据积累之后再收紧。从这套链路可靠性设计里我个人收获最大的认知是可靠性不是某一层堆出来的产物而是从监测到纠错、从重传到切换的完整闭环。每一层都要在能力边界内做到最好同时清楚知道自己搞不定时该把问题甩给谁。光链路这个“不省心”的物理介质恰恰倒逼我们把整条链路从被动维修抬升到了预测性维护的状态。这个思路放到机柜里的散热风扇、电源模块乃至PCB上的电容上都是相通的。做这套设计的那些日子我最深刻的体会是很多问题并不会在功能测试阶段暴露往往要等到业务流量真正压满、链路开始抖动的那一瞬间你才会意识到某些保护机制还缺一堵墙。所以如果你正在做类似的高速互连设计建议尽早把故障注入和劣化模拟加进测试计划里而不是等到现网出事故后再补课。光链路是沉默的但它出问题之前总会留下指纹我们的工作就是把这些指纹提前翻译成告警而不是事后去拼凑现场监控画面。