1. 卡在80%的根源:不是通道不好,是路由逻辑太天真
先交代一下背景。我之前负责一个面向海外市场的消息平台,主要业务是给东南亚、拉美、非洲这些地区的用户发验证码、营销通知和订单提醒。上线头三个月,整体送达率一直趴在78%到82%之间浮动,雨季甚至能掉到74%。一开始我的判断和大多数人一样:通道商不行,换更贵的、声称覆盖更好的通道商总能解决。
结果换了两家之后,送达率曲线几乎没动。该收不到的照样收不到,只是失败的原因从"超时"变成了"未知错误"。这时候我意识到一件事:在那些运营商网络基础薄弱、信令交互异常复杂的市场里,单条通道的覆盖能力上限就摆在那里,你换一百家供应商也是在同一个物理世界里发短信。真正能撬动送达率的地方,在路由系统。
所谓路由系统,本质上就是一个决策模块,它决定一条短信从你的平台发出之后,走哪条通道、以什么优先级、在什么时间点发、失败之后怎么办。听起来简单,但80%的送达率瓶颈,恰恰是因为很多团队的"路由"就是一个查表逻辑——把通道商按价格排个序,便宜的优先,挂了换下一个。这种思路在日活十万、以国内三网为主的场景可能够用,放到全球场景里,就是把复杂的概率问题简化成了排序问题,不吃亏才怪。
我的核心结论先说:全球短信路由的本质不是选通道,而是做概率预估。 你永远无法保证某条通道在某个时刻对某个目标号码一定成功,但你可以根据历史数据和实时信号,估算出哪条通道最有可能成功。把送达率从80%提到95%以上,靠的不是某一家神仙通道,而是把路由从"查表排序"升级成"概率决策"。
这个认知转变是整个项目的分水岭。后面所有工程改造,都是围绕"如何更准确地预估每条通道对每条消息的送达概率"展开的。
1.1 上线前最容易被忽略的统计口径问题
在谈路由之前,先得把"送达率"这个数字本身拆清楚。我见过太多团队一开口就是"我们送达率90%",问一下分母是什么,支支吾吾说不清楚。统计口径不统一,所有优化都是对着错误的KPI使劲。
我们的口径经历了三轮修正:
第一轮,初始口径是"通道商回执状态为DELIVRD的消息数 / 提交通道的消息总数"。这个口径最大的坑在于,很多通道商对"送达回执"的定义非常宽松。有些通道商在消息进入运营商网关时就回DELIVRD,根本没等手机终端确认。这就导致实际到达率可能只有80%,报表上却显示95%。
第二轮,我们把口径改成了"目标手机终端确认收到 / 提交总量",也就是要求运营商回执必须是"终端成功送达"级别。这个口径修正之后,真实送达率直接掉到76%,数字变得难看,但这才是一切优化的基准线。数字难看不可怕,可怕的是对着虚高的数字自我安慰。
第三轮,我们又加入了一个维度:有效提交率。也就是"成功提交到通道商网关的消息 / 平台收到请求的消息"。这解决了另一个问题——平台自身的限流、超时、参数错误导致消息根本没发出去,这些不应该算在通道头上。算清楚这个之后,才能区分问题出在平台侧还是通道侧,这一点对后面定位故障至关重要。
如果在做优化之前,你的团队还在拿通道商提供的回执报表当唯一依据,建议先停下来,把口径统一到"终端确认送达"级别。否则后面一切A/B测试和灰度实验都是空中楼阁。
1.2 失败类型的第一次重新分类
把口径修正之后,我开始对失败回执做精细化分类。大多数路由系统只区分"成功"和"失败"两类,这是另一个隐性瓶颈。失败的成因天差地别,有的可以靠重试解决,有的再怎么重试也是浪费。
我把失败回执归成了五类:
- 号码类失败(如"ARZR"到达限制、空号、停机):这类失败与通道无关,只能靠号码清洗规避。
- 运营商临时性失败(如"系统错误""瞬时段错误"):大概率是网关临时抖动,可重试。
- 签名/模板类失败:通道商侧模板未报备,重试也是白搭,需要走报备流程。
- 超时类失败:通道商长时间未回执,可能是信令链路拥堵,可考虑换通道重发。
- 静默失败:最阴险的一类。通道商不回执,既不说成功也不说失败,消息石沉大海。这类只能靠超时机制兜底。
这五类失败的处理策略完全不同。把失败笼统地放进一个队列里统一重试,既浪费通道资源,又拉低了真正需要重试消息的处理速度。对失败类型做分层处理,是路由系统精细化的第一步。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 智能路由评分:把"经验判断"变成"可计算权重"
知道了问题的维度,接下来就是核心工程:路由决策模型。
最早的版本,路由逻辑用了一个简单的优先组概念。每个通道商有优先级,优先级高的通道先发,发失败就降级到下一优先级。这种设计在通道数少、地域集中的时候还算好用,但在全球多通道场景下,有几个致命缺陷:
- 优先级是静态配置的,通道质量变了,配置没变。
- 只考虑"有没有能力发",不考虑"这个目标号码在这个通道上是否大概率能成功"。
- 没有时间维度,凌晨三点的通道状态和白天的通道状态被认为是同一回事。
我要做的,是把路由决策从"优先级选择"变成"多维评分"。简单说,就是对每一条候选通道,根据当前上下文算出一个分数,分数越高,被选中的概率越大。
2.1 路由决策需要哪些维度的数据
评分模型的特征选择,是当时我们反复推敲的地方。特征太多,模型维护成本高,训练数据不足容易过拟合;特征太少,决策又不够精准。最后我们沉淀了七个维度:
通道历史送达率(权重最高)。按目标国家、目标运营商、时间段三个维度分别统计。注意这里的关键点是"按运营商拆分"。同一个通道商在不同国家的送达率差异非常大,混合统计会淹没局部异常。比如某通道商在印尼整体送达率90%,但在印尼的Indosat网络下可能只有70%。如果不拆分,路由系统会一直往这条通道上发Indosat的消息。
通道实时健康度。基于最近5分钟内的回执延迟和成功率计算。这个维度的作用是为了捕捉瞬时的通道故障。比如某个通道商因为线路割接或网关过载,成功率在几分钟内跳水,健康度会快速衰减。
历史响应延迟。运营商回执的平均耗时。这个指标乍看和送达率无关,但响应延迟过长的通道往往意味着信令链路拥塞,消息虽然最终能到达,但用户体验极差。对验证码类消息,延迟超过60秒基本等于失败。
目标号码的运营商归属。同一个号码段在不同通道上的表现差异明显。有些通道商专门优化了与某家运营商的对接,这条通道对该运营商的到达率显著高于其他通道。
消息类型。验证码、营销通知、OTP的消息权重不同。营销类消息对送达率要求相对宽松,但验证码类消息对实时性和可靠性的要求极高。不同的消息类型应该走不同的通道组合。
时段特征。同一个国家,白天和凌晨的通道质量完全不一样。部分运营商在夜间会做信令降级,或者通道商在夜间切换维护窗口。时段特征主要是为了让模型识别出这种周期性变化。
成本权重。前面六个维度都指向"成功率",但成本不能完全忽视。否则模型会一直选择最贵的通道,虽然送达率上去了,业务利润会被打穿。合理的做法是把成本作为一个弱惩罚项,在成功率接近的通道之间,优先选更便宜的。
这七个维度看起来不难,但真正的坑在于数据怎么来。历史送达率和响应延迟需要长期的回执数据积累,实时健康度需要有一个快速的回执统计管道,运营商归属需要维护号码段数据库。这些数据工程的工作量,往往比模型本身大一个数量级。
2.2 一个可落地的评分公式与衰减机制
模型设计上,我们没有一上来就用机器学习,而是先用了一个带权重的启发式评分公式。公式本身很简单:
code复制score = w1 * 历史送达率 + w2 * 实时健康度 + w3 * (1 - 归一化延迟) + w4 * 运营商匹配度 + w5 * 时段置信度 - w6 * 成本系数
每个维度先做归一化,再乘以权重求和。权重是通过历史数据的回归分析粗略标定的,不是拍脑袋定的。比如我们观察到,在印尼市场,实时健康度的权重应该高于历史送达率——因为那个市场通道状态波动剧烈,历史数据太陈旧了,参考价值有限。而在北美市场,历史送达率的权重应该高一些——通道状态相对稳定,历史数据的信噪比更高。
时段的处理,我们没有直接用"当前小时"作为特征,而是引入了"时间衰减窗口"。具体做法是:计算当前时刻的送达率时,给最近的数据更高的权重,越久远的数据权重越低。衰减函数选的是指数衰减。
code复制衰减权重 = exp(-0.5 * 当前时刻与数据点时刻的小时差)
这样既保留了历史数据的统计意义,又不会让三个月前的通道状态影响今天的路由决策。说句题外话,很多团队在这个问题上栽过跟头——直接把历史数据的平均值拿来做评分,结果通道质量已经好转了,但路由系统还在按"旧印象"少选它。
选通道的时候,我们不能只选出分数最高的那一条。现实情况是,分数最高的通道可能恰好在这个瞬间被并发打满,或者它和分数第二的通道是同一家运营商的两个产品线,走法一致,分散风险毫无意义。所以我们在评分之后加了一个"候选通道池"的选择逻辑:每次路由时保留排名前五的通道,按score占比做加权随机选择。这既保证了整体上优选通道用的多,又保留了一定的探索性和容错性。
3. 通道健康度画像:用滚动窗口感知运营商真实状态
先把评分模型的事放一边,聊聊通道健康度。这应该是整个系统里最容易被低估的模块。
通道健康度听起来不就是"最近一小时成功率嘛",但真做起来,这里面的细节能写一整篇。尤其是如果你接入了五六家通道商,每家对接的运营商还有十几个,你面临的不是一个健康度,而是几十上百个通道-运营商组合的健康度。
3.1 状态回执延迟与送达误判
第一个坑就是回执延迟带来的误判。
大部分通道商的回执不是实时的,而是有一个延迟窗口。短的几秒钟,长的能到几分钟甚至几小时。如果你只根据当前时刻的回执来判断送达率,那么最近几分钟内发出的消息大部分都还处于"等待回执"状态,算出来的成功率会严重偏低。低到什么程度?一个真实案例:某通道商某天的实际送达率是90%,但如果只看最近5分钟的回执,成功率只有40%。
因为我们把"已发送但未回执"的消息默认为失败,而这个通道商恰恰回执延迟较长。结果就是路由系统判断这条通道"正在故障",把流量都切走了。实际上它根本没故障,只是回执慢。
这个问题我当时的解法是引入了一个"在途等待"概念。在计算健康度时,把最近8分钟内已发送但未回执的消息单列出来,不纳入成功率分母,同时单独计算"回执等待率"。如果回执等待率持续居高不下,再判定为通道异常。这个改动至少消灭了三分之一的误切换事故。
3.2 健康度快照的维护与推送
健康度快照的另一个关键点是"国家 x 运营商"的粒度。
刚开始我们也犯过"全局单指标"的错误——给每个通道商维护一个整体健康度。结果某通道商在菲律宾的GLOBE网络下全线故障,但在SMAART网络下表现正常。整体健康度被拉低之后,路由系统把SMAART的流量也切走了,等于用户被迫放弃了一条本可以正常使用的通道。
正确的粒度是"通道商 x 国家 x 运营商"三个维度组合。每个组合维护一份滚动窗口统计。实现上,我们用的是滑动窗口加过期淘汰。窗口中存最近2000条消息的回执记录,每条记录保留"发送时间、回执时间、成功/失败"三个字段。统计时只算窗口内的数据。
实现里有一个精巧的小点:窗口数据不用每收到一条回执就全量重算。我们维护了三个累计值(成功数、失败数、总数),新回执进来时累加,旧记录过期时减掉。这样每个组合的健康度计算是O(1)复杂度,即使同时维护上千个组合,CPU开销也很低。
还有一个实用的细节:健康度快照要定期推送给路由决策模块,而不是让路由模块实时去查询。因为路由决策每秒钟要处理成千上万条消息,如果每条消息都要去查一遍健康度,存储的压力会很大。我们的方案是:健康度模块每10秒生成一份快照,放进内存缓存。路由模块读取缓存,最多落后10秒。在对实时性要求没那么苛刻的短信场景里,10秒的延迟完全不影响效果。
4. 重试、去重与熔断:送达率提升的三大配套工程
路由评分决定了一条消息"走哪条通道",但送达率的完整链路还包含"发失败了怎么办"。重试策略、去重机制和熔断保护,这三块如果做不好,再好的路由评分也会被冲垮。
4.1 幂等与超发防护
先说出乎意料但必须先解决的问题:重试导致的重复发送。
在短信场景里,客户端或上游业务方经常会因为超时而重试。如果没有幂等机制,同一条验证码可能被发送两遍、三遍,甚至更多。这不仅造成通道资费浪费,更重要的是,用户收到的第一个验证码可能已经过期,后续的验证码会造成严重困惑和安全隐患——注意,我在生产环境见过不少因为重复验证码引发的账号绑定错误事件。
幂等方案我们做了两层:
第一层是业务幂等。每条短信在接入层生成一个全局唯一的message_id,映射到业务方的request_id。相同request_id的重复请求,在网关层直接返回上一次请求的处理结果,不再重复提交通道。
第二层是通道去重。对于已经提交通道但通道回执超时的消息,重试时不能直接用同一个message_id重新提交,因为通道商可能已经在处理这条消息了。我们会在原message_id基础上追加一个重试序号,比如"msg_123_retry_2",这样既能让通道商区分这是一次新的提交,又能通过原始message_id串联全链路日志。
4.2 指数退避重试策略
重试策略也是走过弯路的。最初我们用的是固定间隔重试,失败后等30秒再发一次,最多重试三次。效果不好——某些故障窗口长达几分钟,30秒后重试时故障还没恢复,三次重试全部失败,消息最终被放弃。
后来换成了指数退避加抖动(jitter)。具体的参数是:
- 第一次重试间隔:15秒
- 第二次:45秒
- 第三次:2分钟
- 第四次:5分钟(这是上限)
每次间隔在基础值上乘以一个0.8到1.2之间的随机系数,防止大批消息同时重试造成通道侧流量尖峰。这个"抖动"非常关键,不加抖动的指数退避会让系统在故障恢复时形成明显的雷群效应,所有重试消息同时涌入,反而把通道打挂。
另外还有一个容易被忽略的策略:重试时要重新走一遍路由评分。很多系统的重试逻辑是"换下一条通道",这没错,但换通道的决策不应该简单按顺序轮换,而是应该在上一次失败的基础上,把失败通道的健康度降权,然后重新计算所有候选通道的分数。举个例子,原来通道A得92分、通道B得88分。消息在A上发送失败后,把A的实时健康度调低,降低它的分数到70分,B就变成了本轮最优。这样重试决策和初发决策共用同一套模型,逻辑一致,维护也简单。
4.3 熔断与降级联动
熔断机制是最后一道防线,用于防止故障通道被反复调用。
我们的熔断阈值设定是:一个通道-运营商组合,最近10分钟内成功率低于40%,且样本量大于50条,直接熔断30分钟。 为什么要加"样本量大于50条"这个条件?因为如果你的消息量很小,10分钟内只有两三笔失败,随机性太大,不能作为熔断依据。样本量阈值确保熔断决策有统计意义。
熔断期间,路由评分模块直接把这个组合的分数强制设为-1,等于从候选池中移除。熔断结束后,先放5%的探针流量进去,观察恢复情况,再逐步放量。这个"缓恢复"过程很关键,直接全量恢复的话,等同于没有熔断。
还有一个联动细节:熔断不仅仅是路由侧的事情,还要把状态反馈给计费模块和监控告警模块。熔断期间产生的消息在账单上需要单独标记,否则故障期间产生的计费数据会污染后续的成本分析。监控告警则是为了触发人工介入——有些故障不是自动恢复能解决的,需要通道商的客服处理,而人工介入是有时间成本的,越早发现越好。
5. 灰度上线的数据效果与实际踩坑
从理论到落地,这个系统从设计、开发到上线,前后用了六周。我没法说它是一个轻松的过程,中间踩了不少坑,而且有些坑是数据出效果之后才暴露的。这章讲一下上线之后的部分真实数据,以及几个有代表性的踩坑复盘。
5.1 灰度对比数据
灰度方案是:选取印尼和墨西哥两个国家,每个国家各取10%的流量,跑新的智能路由评分模型。对照组的逻辑还是老的"优先级排序"方案。灰度周期是七天,因为要覆盖一周内的生产周期波动,比如工作日和周末的通道质量差异。
七天之后,两组数据对比:
| 指标 | 对照组(旧路由) | 实验组(新路由) |
|---|---|---|
| 印尼整体送达率 | 81.3% | 92.7% |
| 印尼验证码类送达率 | 76.9% | 94.1% |
| 墨西哥整体送达率 | 78.2% | 89.8% |
| 平均响应延迟 | 14.2秒 | 9.6秒 |
| 单条消息平均成本 | 0.031美元 | 0.034美元 |
送达率提升超过10个百分点,验证码类消息提升尤其明显,从76.9%到94.1%。当时我看到这个数字,第一反应不是高兴,而是怀疑:是不是对照组被刻意压低了一种。于是去翻了对照组,确认它在灰度期间没有任何配置变更,通道商列表和优先级都和上线前一致。所以提升是真的。
成本也涨了,单条成本上涨约10%。原因是模型倾向于选择覆盖率更好、成功率更高的通道,这类通道往往不是最便宜的。当时我们内部讨论过是否要接受这个成本涨幅——最后业务方拍板:验证码类消息的送达率每提升1个百分点,对新增用户留存的影响是显著正向的,所以成本涨幅可以接受。但对于营销类消息,我们会把成本权重调高,因为这些消息对时效性和送达率的敏感度要低一些。
5.2 三个有代表性的踩坑记录
坑一:时区问题。 灰度上线后第三天,墨西哥的送达率突然跌到82%,比对照组还低。排查了半天,发现是时段特征那里出了问题。我们把服务器时间按UTC算,用"发送时刻的UTC小时"作为时段特征。但墨西哥城的用户作息是按当地时间的,UTC凌晨3点对墨西哥来说是晚上9点,正是用户活跃时段,通道负载高。模型用UTC小时做特征,等于把墨西哥的黄金时段误判成了深夜低峰时段。修复方式是把发送时刻换算成目标国家本地时区,再取小时值。这是一个典型的"全球系统没有本地化视角"带来的问题。
坑二:热通道的自我实现困境。 新路由上线两周后,出现了另一个问题:某个印尼运营商的最佳通道稳定地占据了80%以上的出货量。表面上看起来很好,但风险是过度集中在单条链路上。某天该运营商因为网络调整,这条通道成功率骤降,系统花了接近15分钟才把流量切走,期间大量验证码延迟到达。这个问题的根因在于评分模型只考虑了历史成功率,没有考虑"通道分散度"。后来我们加入了一个惩罚项:当某条通道的市场份额超过50%时,给它的分数乘以0.8。这样即使它仍然是最好的选择,系统也保留了30%-40%的流量给次优通道,故障时的切换速度会更快。
坑三:结果回执的"假成功"。 提到"假成功",这是我最想分享的一个坑。我们在做数据复核时发现一个诡异现象:某个通道商在深夜时段成功率反而比白天高。按理说,深夜用户不活跃,信号环境可能不好,成功率应该低才对。后来日志分析发现,这个通道商在深夜把一部分消息"吞"了。它没有将消息递交给运营商,而是直接把回执改成DELIVRD返回给平台。后来我们专门为这个通道商增加了一条静态规则:该通道商只允许用于日间流量,夜间一律流量迁移到备用通道。这种"假成功"很难从路由系统内部发现,必须定期做人工事件洞察。所以,我保留了一个习惯:每周手动审查单个通道商的"成功回执率 vs 运营商实际接收量"的差异。一旦发现异常,先把流量切走,再去跟通道商沟通。
这三个坑,各自对应了路由系统的一个核心问题:时区特征是本地化视角的代表,通道分散度是稳定性与容错的平衡,假成功回执是数据可信度的底线。都值得在架构设计阶段就提前考虑进去。
写到这里,这套系统的整体思路算是讲完了:从统计口径修正,到路由评分模型,到通道健康度画像,再到重试熔断三件套,最终落到灰度验证和踩坑复盘。如果只能用一句话总结我的个人体会,那就是:全球短信路由系统不是一个"选择通道"的工具,它本质上是一套持续感知全球通信网络状态、并在不确定性中做概率决策的系统。 这也是它能从80%提升到95%以上的底层原因。
