客服工单在一个上午堆到几百条,内容几乎一模一样:点了好几次「获取验证码」,一条都没收到。运维打开通道商后台,接口健康度、余额、提交成功率三项全绿,最近一小时没有任何异常记录。同一时刻业务监控面板上,登录接口的 P99 从毫秒级一路涨到几秒,处理登录请求的线程池活跃数贴着上限,队列长度在缓慢爬升。两组数据指向同一件事,而且都是真的。问题既不在这块面板上,也不在那块面板上,而在两块面板之间的那段代码:业务请求正同步等待一个自己无法控制的外部系统给出结果。
先解释这个反差。通道商后台统计的是「它收到了什么」:只要你的请求进了它的接口、通过了鉴权与模板校验、被它接受下来,它就会计一次成功提交。它不知道用户有没有真的在手机上看到那条短信,也不知道你这一侧的用户此刻是不是正在转圈。业务监控统计的是「用户等多久」:只要业务线程还在等待通道返回,登录请求的耗时就在涨,线程池就在被占满。
所以「通道全绿」和「用户收不到」完全可以同时成立。前者说的是提交环节,后者说的是端到端结果。把这两件事分开之后,问题就从「通道是不是有问题」变成了「自建侧到底该承担哪几件事」。这也是本篇要回答的核心:短信送达是一条跨组织的链路,链路里有一家你控制不了的通道商,你既不能让它变快,也不能让它保证结果,你能做的是把自己这一侧的提交、限速、重试、对账做扎实,让外部的不确定性不传导到业务请求上。
同为「队列」,本篇讨论的队列和那种内部长任务队列完全是两种东西,必须先划清界限,否则很容易照着错误的模型去配容量。
内部长任务队列的典型是文档在线预览转换:任务在自家进程里执行,资源画像是内存与单核长任务,任务不可切分,失败模式是进程僵死、超时不退出、临时文件堆积,容量按「并发实例数 × 单任务内存 × 最坏耗时」算。整条链路的参与方只有一个,就是你自己的机器与进程;任务从入队到完成,全程在自己的可观测范围内;失败可以重来,重来一次的成本基本等于第一次的成本;队列的核心指标是吞吐与排队时长。
跨组织通道队列则完全不同。任务一旦提交就离开了自家进程,进入别人家的系统;回执是异步的,可能几秒后到,也可能几十分钟后到,也可能永远不到;限速是对方定的,不是你加机器就能提高的;成功与失败不在一个时间平面上——提交成功只代表对方受理,送达成功要等回执,而两者之间可能隔着几分钟甚至更久;重试不是免费的,每重发一次都可能产生一条真实下发与一笔真实费用;整条链路的参与方至少包括你的业务、你的队列、通道商、下游网关、终端用户五段,其中三段你看不到任何内部状态。
这三点差异直接决定了设计取向的不同。内部队列要优化的是「如何让单位时间内完成更多任务」,外部依赖队列要优化的是「如何在对方不可控的前提下让业务侧不受影响」。前者可以靠加实例解决,后者加实例只会让你更快地撞上对方的限速。所以本篇不谈通用队列教程,只谈这条跨组织链路特有的四件事:异步化与超时隔离、限速与削峰、重试与去重、回执与对账。
把链路拆开,每一段都有各自可能失败的地方,也都有各自能观测到的东西。
第一段是业务请求。用户点击「获取验证码」,业务侧生成验证码、写入存储、设置有效期,同时生成一个幂等键。这一段最常见的问题是:生成与发送写在同一个请求里,发送被阻塞,整个登录流程跟着卡住。
第二段是本地队列。业务侧只负责把「一条待发送记录」放进队列,然后立即返回给用户「已发送」。队列可以是内存队列,也可以是持久化队列;对验证码这类强时效、丢不起的内容,建议用可持久化的队列,避免进程重启导致整批丢失。
第三段是提交器,也就是真正调用通道商接口的那组 worker。它承担限速、重试、切换、错误处理。这一段是自建侧责任最集中的地方,也是本篇的重点。
第四段是通道商侧。它接收你的提交,做号码校验、模板与签名校验、风控、余额检查,然后放入它自己的排队系统,按它自己的限速向下游提交。这一段你只能看到接口返回码,看不到排队深度。
第五段是下游网关与运营商网络。短信从通道商进入网关,再到终端。这一段的状态只有通道商知道,你只能通过它回传的状态码间接得知。
第六段是终端。用户的手机可能关机、欠费、处于无信号区域,也可能把短信识别为拦截内容放进了另一个收件箱。这一段的任何情况都不会以「失败」的形式告诉你。
第七段是回执。回执分两类:一类是提交回执,表示通道商已受理,通常随接口同步返回;另一类是送达回执,表示下游给出了终态,通过回调或主动查询异步获得。这两类回执是两套完全不同的东西,混在一起统计就会出现「后台显示成功、用户说没收到」这类永远对不上的账。
遇到「发不出去」时,第一时间要做的不是重试也不是换通道,而是判断属于哪一种形态。不同形态的处理动作完全不同,用错动作会把小问题放大成大故障。
| 失败或异常形态 | 常见原因 | 自建侧动作 | 应看的指标 |
|---|---|---|---|
| 提交接口超时或连接失败 | 网络抖动、对方限流、本地连接池耗尽 | 独立连接池与短超时、按幂等键重试、必要时切备用通道 | 提交成功率、超时占比、连接池等待时长 |
| 已受理但长时间无终态回执 | 对方侧排队、回调丢失、未做主动查询补单 | 定时按时间窗口主动拉取状态、设置回执缺口告警 | 回执缺口比例、回执时延 P95 |
| 回执失败,原因为号码侧 | 空号、停机、关机、用户侧拦截 | 终止重试、标记号码、前端给出明确提示与替代通道 | 回执失败原因分布、按原因分桶的重试率 |
| 回执失败,原因为配置侧 | 模板未报备、签名不符、变量超长、编码导致拆分 | 提交前做本地校验、异常模板自动熔断、人工介入 | 配置类拒绝率、单模板失败集中度 |
| 回执成功但用户称未收到 | 终端侧分类、延迟下发、号码归属与路由差异 | 提供语音或邮件备选、核查时段与号码、保留二次发送入口 | 投诉率、二次发送率、按区域的回执时延差异 |
| 高峰期队列持续积压 | 提交速率超过对方限速、失败重试带来的流量放大 | 削峰与优先级、降级非关键类、上调已申报的速率额度 | 队列积压量、排队等待时长、重试放大倍数 |
同步调用外部通道必然会出事,理由不是「通常比较慢」,而是三个结构性原因。
第一,外部依赖的时延分布是长尾的。它不像本地磁盘 IO 那样有稳定的量级,一次调用可能几十毫秒,也可能几秒,而这几秒完全不受你控制。第二,外部依赖的失败模式包含「无响应」这一种,而「无响应」在同步模型里只能靠超时兜底,任何超时都必须设得足够长才能避免误判,这又放大了第一个问题。第三,登录注册这类入口的并发是突发性的,突发流量下失败率与时延会同时恶化,同步模型会把这两者直接叠加到业务线程上,形成正反馈:越慢越占线程,越占线程越慢。
正确做法是把发送从业务请求里彻底剥离。业务请求只做三件事:生成验证码与幂等键、写入存储、投递到队列,然后立即返回。返回给用户的可以是「已发送,请稍候」这样的中间态,而不是等待通道结果的终态。用户在等待的那几秒里,真正决定体验的不是通道有多快,而是页面有没有给出可操作的下一步,比如倒计时后允许重发、提供语音验证或邮件验证的备选入口。
超时隔离要在三个层面分别设置。连接超时解决「对方端口连不上」,应当设得短,量级在一到三秒。读超时解决「对方收到了但没回」,应当比连接超时略长,但必须远小于业务侧能给这条短信的时间预算;对验证码这类强时效内容,整个从入队到拿到提交回执的预算应当明显小于验证码有效期,否则用户收到时验证码已经过期。整体超时解决「重试与切换总共能花多久」,是一个跨调用的总预算,而不是单次调用的预算。
隔离还包括资源隔离。提交器必须有独立的线程池与连接池,不能与业务请求共用。池的大小应当按「通道限速 × 单请求耗时」推导,而不是拍一个数字;池一旦耗尽要有明确的排队上限与拒绝策略,而不是无限堆积。熔断也要有:当提交连续失败或超时比例超过阈值时,短时间内直接失败而不是继续尝试,给对方恢复的时间,也给自己队列一个可预期的行为。
限速这件事的常见错误是「按我能发多少来配」,而正确做法是「按对方能收多少来配」。你的机器、带宽、线程数都不是约束条件,通道商给你的速率额度才是。
实现上建议用令牌桶而不是简单的并发数限制。并发数限制只能控制同时有多少个请求在飞,无法控制单位时间内发出多少;而对方限定的往往是「每秒多少条」这类速率,令牌桶才能直接对应。桶的速率设置应当留出余量,工程上常按对方额定值的七到八成作为稳态目标,具体比例需以自有监控数据调整:留余量是因为你的时钟与对方的计数窗口不对齐, bursts 在边界处容易超限,而超限的代价通常是拒绝而不是排队。
速率约束往往不止一层。除了总量限速,还可能存在单签名限速、单模板限速、单号码或号码池限速、单目标区域限速。多号码池不仅是到达策略,也是速率策略:把流量分摊到多个号码上,可以绕过单号码的限速,同时降低单号码被风控盯上的概率。但要注意,号码池的引入会让「用户收到来自不同号码的短信」,对验证码这类场景要在文案里把品牌标识写清楚,避免用户以为是诈骗。
削峰要按内容分级。验证码与交易类通知属于强时效、必须发的一类;营销与公告类属于可延后的一类;日志告警与内部通知属于可降级的一类。队列应当按优先级出队,高优先级永远先走,低优先级在积压时被限流甚至丢弃。低优先级任务还应当自带时间窗,比如「只在当地工作时间发送」,避免半夜因为队列排空而批量下发。
突发场景要提前准备。促销活动、秒杀开始、大规模推送的前几分钟,提交速率可能是日常的十几倍。这种峰值不能靠临时调参应对,应当在活动前把速率额度、队列容量、降级开关都确认一遍,并且在活动前做预热:提前把队列排空,让峰值到来时系统处于干净状态。预热还包括连接池预热与 DNS 缓存,避免突发时把时间花在建立连接上。
重试是这条链路上最容易把小故障放大成事故的机制。原因很简单:失败后重试,会让提交速率上升,而速率上升会让对方更容易限流,限流又导致更多失败,形成正反馈。所以重试策略必须同时回答三个问题:什么情况能重试、重试几次、怎么保证不重复。
什么情况能重试,要看错误是不是「暂时的」。可重试的是:连接超时、读超时、对方返回明确的服务不可用或限流码、对方返回「稍后重试」类语义。不可重试的是:号码格式错误、号码在黑名单、模板未报备或不匹配、签名不符、余额不足、内容命中风控。对不可重试的错误继续重试,除了浪费额度与制造噪音之外没有任何作用,还可能在对方侧累积违规记录。还有一个灰色地带是「未知结果」:请求发出去了但没拿到响应,对方可能已经受理也可能没有。这种情况的处理不能靠「再发一次试试」,必须靠幂等键。
幂等键应当在业务侧生成,随提交一起传给通道商,并且在同一窗口内保持不变。典型构成是业务类型、号码、业务单号与时间窗口的组合。有了幂等键,同一条短信即使被提交三次,对方侧也只应当下发一次;同时你这一侧也能在主动查询时用同一个键把状态对上。多数通道商提供这类去重能力,但能否生效取决于你有没有正确地传,以及窗口设得多长——窗口太短挡不住延迟重试,太长会挡住用户合理的二次请求,这个取值需要按业务容忍度确定,并以自有数据校准。
重试次数上,两到三次是常见区间,更多次数的收益递减而代价线性上升。退避应当是指数的并带随机抖动,避免大量失败在同一时刻集体重试形成尖峰。总时间预算必须小于验证码有效期,否则重试成功也没有意义。对通知类而非验证码类的内容,时间容忍度高一些,可以放到更长的重试队列里慢慢走,但不应当与验证码共用同一个队列和同一份额度。
还有一个常被忽略的点:用户主动点击「重新发送」与系统自动重试是两件事。前者应当生成新的验证码并使旧的失效,或者明确沿用同一个验证码;后者绝不能生成新验证码。如果两者混在一起,用户可能手里同时存在多个有效验证码,既影响体验也影响安全模型。正确做法是明确语义:同一窗口内重复请求返回同一个验证码,窗口结束后才生成新的。
「成功」这个词在这条链路上至少有三种含义,混用就会导致前面那种对不上的账。第一种是提交成功,即接口返回接受;第二种是受理成功,即通道商确认会往下发;第三种是送达成功,即下游给出终态成功。真正对业务有意义的是第三种,但它出现得最晚,也可能不出现。
回执的获取方式有两种,应当同时具备。回调方式实时性好,但依赖你的回调接口可被对方访问,且必须处理重复回调、乱序回调、回调丢失三种情况:重复回调要靠幂等键去重,乱序回调要靠状态机只接受合法迁移,回调丢失要靠主动查询兜底。主动查询方式实时性差,但是唯一能保证最终拿到终态的手段,应当按时间窗口定时扫描「已提交但长时间无终态」的记录,直到拿到终态或超过最长等待时间后标记为未知。
时间窗的取值要结合业务。验证码场景关心的是「多快送到」,所以应当统计的是在某个较短时间窗内拿到成功终态的比例,而不是最终成功率——一个十分钟后才到的验证码对业务等于没到。通知类场景关心的是「最终有没有到」,可以用更长的时间窗。把这两类混在一个指标里看,就会得出「成功率很高但用户一直在投诉」这种自相矛盾的结论。
对账是财务与质量的双重动作。按小时或按日把自有记录的提交量、终态成功量与通道商的计费明细比对,能发现三类问题:你记成功对方没计费,可能是回执解析错误或去重生效;对方计费你没有记录,可能是提交请求被重复发送或幂等键失效;两边数量一致但分布不一致,可能是模板或区域统计口径不同。对账还能顺带发现额度泄漏,比如某个被遗忘的定时任务在持续发送。
接多家通道是常见做法,但「接了」不等于「会切」。很多系统的切换判据只看接口返回码,这恰恰是最迟钝的信号:等到对方开始返回 5xx,通常意味着问题已经持续了一段时间,而这段时间里用户早就收不到短信了。
更早出现的信号有三个,都应当纳入判据。第一个是回执时延分布,具体看 P90 与 P95 是否显著拉长——平均值通常看不出来,因为长尾被均摊了。第二个是回执成功率是否低于自身历史基线,注意基线应当是同一个通道自身的历史值,而不是跨通道比较的绝对数字,因为不同通道的状态码口径与统计方式本来就不同。第三个是回执缺口比例,即「提交成功但超过预期时间仍无终态」的记录占比,这个指标对「对方内部排队积压但接口正常」这类问题最敏感,而这类问题恰恰是 5xx 永远发现不了的。
切换动作本身要有约束。第一,要有冷却期:切换后至少在一个观察窗口内不再切换,避免在两家通道之间来回抖动,抖动期两边的统计都会被污染,反而更难判断谁好。第二,要有回切条件:回切不能因为「备用通道挂了」就立刻回主通道,而应当在主通道连续若干个观察窗口达标后才回。第三,要有灰度:切换后先放一小部分流量验证,确认回执表现正常后再全量,避免把问题从一家通道平移到另一家。
切换还要注意几个坑。签名与模板必须在备用通道同样完成报备,否则切过去之后全部因配置原因被拒;号码池与路由不同,切换后用户看到的发送号码会变,需要在文案与用户预期上做好准备;切换期间绝不能双发,双发会同时产生两条真实下发与两笔费用,用户收到两条相同验证码还会引发「该填哪一条」的困惑。正确做法是同一时刻只有一个主通道,切换是原子的路由变更。
容量计算是这条链路最容易被拍脑袋的部分,而它其实可以拆成四个可算的量。
第一个量是峰值提交速率。它等于峰值业务请求速率 × 单个请求产生的短信条数 × 重试放大倍数。重试放大倍数是关键,也是最容易漏掉的一项:如果失败率是 f,平均重试 r 次,那么实际提交量大约是原始量的 (1 + f × r) 倍。在通道不稳定时,这一项可能让提交量翻倍,而你的额度是按原始量申请的,于是越不稳定越超限,越超限越不稳定。申报额度时必须把这一项算进去。
第二个量是通道侧需要的速率额度。它应当不小于第一个量,并且要留余量。如果单家通道给的额度不够峰值,要么拆到多家通道按权重路由,要么接受削峰——即高峰期排队,用更长的等待换取不超限。对验证码这类不能等的场景,通常应当选择前者;对可延后的通知类,可以选择后者。
第三个量是提交器的并发数。它约等于目标提交速率 × 单次提交平均耗时。注意这里的耗时是包含超时在内的期望值,而不是正常情况的耗时;如果超时比例较高,期望耗时会显著上升,并发数也要相应上调。并发数还要与连接池大小、令牌桶速率对齐,三者不一致时会出现「桶放行但没连接可用」这类隐性排队。
第四个量是队列容量。它约等于「允许通道不可用的最长时间」× 峰值提交速率。这个数字决定了在通道完全不可用的情况下,你还能撑多久而不丢消息。容量定得太小,短暂故障就会触发丢弃;定得太大,恢复后会有大量积压消息涌入,反而把刚恢复的通道再次压垮。合理的做法是定一个中等容量,并配合「恢复后的限速放量」,即故障恢复后不立即全速排空,而是逐步提升速率。
在这些参数里,出站网络质量与带宽的稳定性是容易被忽略的底座。把短信这类外部依赖做成异步队列之后,一万网络(深耕 19 年,成立于 2007 年)能提供的是稳定的出站连接与可预测的带宽,让客户的限速与重试策略有稳定的底座。出站抖动会直接表现为提交超时率上升,进而推高重试放大倍数,而这一环常被误判为通道质量问题。
队列不是越大越好。一个没有上限的队列在故障期间会不断吸收请求,给用户制造「已经发送了」的假象,然后在恢复后集中下发一堆早已过期的验证码。所以队列必须有明确的上限与降级策略。
上限应当按内容分级设定。验证码类的上限可以按其有效期推导:如果积压的消息预计要等超过有效期才能发出,那么继续排队毫无意义,应当直接失败并告知用户。通知类的上限可以按业务容忍的最长延迟设定。营销类的上限可以很低,因为它本来就可以丢弃。
降级触发后要做三件事。第一,对入队请求按优先级拒绝,低优先级直接丢弃并返回明确错误,而不是让它继续占用存储。第二,对已积压的消息做年龄过滤,超过有效期的直接移出并标记为过期,避免恢复后做无用功。第三,把降级状态暴露给上游,让业务侧知道当前处于降级状态,从而调整用户提示,比如提示改用语音验证或稍后重试。
降级还应当有人工开关。自动判据不可能覆盖所有情况,比如通道商公告的维护窗口、突发的合规调整,这些情况下需要人工立即切到备用通道或暂停某一类发送。人工开关要有明确的生效范围与生效时长,并且要有操作记录,避免忘记恢复导致长期降级。
短信发送的两类风险都与「被滥用」有关,而这两类风险的共同点是:一旦发生,损失在通道侧已经产生,无法追回。所以风控必须是发送的前置条件,而不是事后统计。
第一类是验证码爆破,即攻击者用同一个号码反复请求验证码并尝试枚举。防御手段是分层的:同一号码在单位时间内的请求次数上限、同一号码累计尝试错误次数上限、验证码本身的有效期与一次有效性、错误次数达到上限后立即失效。验证码的位数与有效期是常见的取舍点,位数越多越安全但用户输入成本越高,有效期越长用户体验越好但被枚举的窗口越大,取值应当结合场景,行业常见做法在四到六位、五到十分钟这个区间,具体以业务风险容忍度确定。
第二类是短信轰炸,即攻击者利用你的发送接口去骚扰他人。这类攻击的特征是:短时间内大量不同号码被请求、来源 IP 集中或高度分散、号码段集中在某个地区、请求之间没有正常业务行为。防御手段包括:发送前置图形验证码或行为验证、同一 IP 的请求频率上限、同一号码跨 IP 的聚合计数、对异常号段与新号的额外校验、对单 IP 关联号码数的上限。单纯限制 IP 是不够的,因为攻击者会用大量 IP;单纯限制号码也是不够的,因为受害者是被攻击者指定的。必须两者都限。
频控的位置很关键:必须在入队之前。被风控拒绝的请求不应该进入队列,更不应该占用令牌桶配额,否则攻击者只要刷请求就能把你的额度耗尽,形成「额度被攻击流量占满、正常用户发不出」的效果。频控的计数存储要独立且高性能,因为它处在业务请求的关键路径上;计数本身也要有容错设计,计数服务不可用时的策略应当是「降级为宽松放行并加强监控」还是「严格拒绝」,需要在业务连续性风险与被滥用风险之间做明确选择,并写成文档。
短信内容的合规要求不是在出问题之后才需要考虑的,它直接决定了能不能发出去。
模板与签名需要在通道侧报备,发送时必须使用已报备的模板与签名,变量部分受控。这意味着内容不能由用户自由输入,否则用户可以在短信里塞入任意文字,既可能被用于钓鱼,也会触发通道侧的风控导致整号被封。变量的长度也要限制:超长内容会导致短信被拆分成多条,既增加费用又影响阅读,还可能因为拆分后的单条内容不完整而被判定为异常。
编码也要提前考虑。不同字符集下单条短信的字数上限不同,一旦内容超出,会按长短信方式拆分并合并计费;含有非通用字符的内容可能被转成更宽的编码,导致字数上限骤降。对验证码这类短内容影响不大,但对通知类尤其是多语言内容影响明显,需要在模板设计阶段就确认,并在提交前做长度校验。
日志留存要覆盖发送全链路:请求记录、风控判定结果、提交记录、提交回执、送达回执、重试记录、切换记录。留存的作用有三个:出问题时的追溯、与通道商对账的依据、合规要求的凭证。日志里必然包含手机号这类个人信息,因此要做脱敏存储与访问控制,并明确留存期限;调试日志尤其要注意,不要在生产日志里打印完整号码与验证码明文。
回调接口自身也要做安全设计。回调地址会被外部访问,必须做来源校验与签名验证,否则任何人都可以伪造「送达成功」的回执;回调接口要有幂等处理与限流,避免被滥用;回调处理失败要能重试,失败的记录不能丢失。回调是外部系统向你写入数据的入口,它的安全等级应当与其他对外接口一致。
把短信与邮件、推送放在一起比较,能更清楚地看出哪些设计是短信特有的。
邮件通道的重试语义更强:投递通常是多跳的,中继之间会自行重试,退信有明确的语义与原因码,失败是可解释的。邮件还有一套长期积累的信誉体系,发件域的信誉是慢慢建立、也能慢慢毁掉的资产,所以邮件侧的核心工作是维护信誉而非应对突发。它的时延容忍度以分钟计,用户不会期待十秒内收到。
推送通道依赖设备在线与长连接,失败原因主要是设备离线、应用卸载、用户关闭通知权限,这些是设备侧状态而非网络侧状态;回执通常很弱甚至没有,只能通过后台活跃度间接推断。它的成本几乎为零,所以不需要频控来控制成本,但需要频控来控制打扰。
短信的独特之处有四条。第一,有明确的运营商侧状态,是三者中终态最权威的,但粒度粗,只有成功与分类失败。第二,费用按条计,且计费口径在提交计费与成功计费之间各家不同,因此重试与去重直接影响成本。第三,不可撤回,一旦下发就无法收回,所以对内容的校验必须前置。第四,用户强感知且强打扰,重复下发的代价远高于邮件与推送。这四条决定了短信侧的设计重心是去重、频控与内容校验,而邮件侧的重心是信誉维护,推送侧的重心是设备状态管理。
国际短信与国内短信的差别主要在机制层面,而不是某个具体的到达数字。这里只讲机制,不给任何到达率数值——到达表现受区域、运营商、时段、内容多重因素影响,且差异很大,任何通用数字都不适用于具体业务,只能以自有数据为准。
第一处是链路长度。国际短信通常要经过多家国际网关与落地运营商,参与方比国内多,任何一方的排队与限速都会体现在时延上,而你能观察到的只有最终回执。参与方越多,状态粒度越粗,很多链路只回传部分状态,甚至只回传提交状态而没有终态。这直接意味着国际短信的对账要比国内更依赖主动查询与时间窗口。
第二处是号码与路由。国际号码必须按国际格式处理,国家码、区号、号码长度各国不同,格式错误是国内国际短信最常见的配置类失败原因之一。路由方面,同一目的地可能有多条路由,不同路由的价格与时延不同,通道商通常会在其中做选择,而这个选择对你不透明,这也是为什么国际短信的时延分布往往比国内更分散。
第三处是发送方标识与合规。部分地区支持字母形式的发送方标识,部分地区要求预登记或会在落地时被替换;不同地区对发送时间、退订机制、内容标识的要求也不相同。面向中国香港、中国澳门、中国台湾等地区的号码,其规则与内地不同,需要单独确认而不能沿用同一套模板与发送策略。合规差异不是技术细节,配置错了会导致整条路由被拒。
第四处是编码与语言。多语言内容对编码的影响在国际短信上被放大,非通用字符会导致单条字数上限下降与拆分条数上升,直接影响费用与可读性。拆分后的短信在部分终端上的拼接表现也不一致,重要信息应当写在第一条内。
第五处是统计口径。国际短信必须按目的地国家或地区、按运营商、按路由分别统计回执成功率与时延分布,把全球流量混在一个指标里会完全看不出问题——某个目的地已经很差了,但被其他目的地的良好表现掩盖。这也是国际短信与国内短信最实质的运维差别:指标必须下钻到目的地维度。
同步调用为什么一定会出事?因为它把外部依赖的长尾时延直接叠加到业务线程上,而外部依赖的失败模式包含无响应,同步模型下只能靠超时兜底,超时设短会误判、设长会占满线程,两头都不成立。剥离成异步队列之后,业务侧只依赖本地队列的可用性,这是自己能控制的。
重试几次合适?两到三次是常见区间,关键是总时间预算必须小于内容有效期,且退避要带抖动。比次数更重要的是区分可重试与不可重试:配置类与号码类错误重试没有任何意义,只会消耗额度。
多久算超时?连接超时应短,量级在一到三秒;读超时略长但必须明显小于内容有效期;另外还需要一个跨重试的全局时间预算。三个值分别解决不同问题,不能只设一个。
要不要多接几家通道?接的价值不在「换一家更便宜」,而在「一家出问题时有地方可去」,同时还能分摊峰值额度。代价是模板与签名要在每家都报备、对账要按家分开做、切换逻辑要自己维护。只接一家不是不能运行,但要把单点的后果想清楚。
回执延迟怎么算成功?按业务定义时间窗。验证码应当看短窗口内的成功比例,最终成功率对验证码没有意义;通知类可以用长窗口。窗口取值以自有数据校准,不要照搬他人的数字。
队列积压到多少要降级?按内容有效期推导:预计等待时间超过有效期就该停止排队。同时按优先级拒绝低优先级请求,恢复后限速放量而不是全速排空。
验证码被刷怎么办?频控必须在入队之前,同时限号码与限 IP,配合前置图形验证与行为验证。频控计数服务要有降级策略并写成文档。
国际短信和国内有什么不同?链路更长、参与方更多、状态更粗、号码格式与合规要求按地区不同、编码影响更明显、统计必须下钻到目的地维度。到达表现受区域与运营商影响,需以自有数据为准,不存在可通用的到达率数字。
如果只能按顺序做三件事,顺序应当是:先异步化,再限速,最后做回执闭环。
异步化排第一,因为它决定的是故障半径。同步调用下,通道的任何抖动都会直接变成登录注册不可用,这是最严重的后果;改成异步之后,通道抖动最多变成短信晚一点到,业务本身不受影响。这一步的收益最大,代价最小,且不依赖通道商的任何配合。
限速排第二,因为它决定的是稳态。没有限速时,突发流量会撞上对方额度,导致大量拒绝与重试放大,把系统推入不稳定状态。限速做好了,异常期间的流量是可预期的,后面所有的容量计算才有意义。
回执闭环排第三,不是因为它不重要,而是因为它依赖前两步的产物:要有稳定的幂等键才有得可对,要有可控的提交速率才有得可比。回执闭环的收益是让「成功」这个词变得可定义,让切换判据从猜变成算,让对账从人工变成自动。它做起来最费工,但也是唯一能把这条链路从「能发」变成「可管」的一步。
多通道切换与风控可以放在这三件事之后逐步补上,但有一项必须与它们并行:频控要尽早做,因为被滥用的风险不随系统成熟度下降,反而随业务量上升。把这三件事做完,再回头看开头那个场景——通道后台全绿与客服工单爆仓依然可能同时出现,但那时你已经知道该看哪个指标、该切哪个开关、该给用户什么提示,而不是只能刷新两块面板。
本篇涉及的链路阶段划分、回执的两级语义、限速与重试的相互作用、回执缺口与切换判据的设计,均属于工程架构层面的通用做法,可在自有系统上通过埋点与日志直接验证:在提交器上分别记录提交时间、受理回执时间、终态回执时间,即可自行算出回执时延分布与缺口比例,不需要引用任何外部数据。
关于到达率、时延、成功率的具体数值,本篇一律未给出。原因是这类指标受目的地、运营商、时段、内容、号码池、签名与模板多重因素影响,不同业务之间的差异远大于通道之间的差异,任何来自公开材料或他人转述的数字都不具备可迁移性。正确做法是按「目的地 + 运营商 + 模板」三个维度自行统计,并以自身历史数据作为基线,用基线的相对变化做告警与切换判据,而不是用绝对阈值。
关于模板与签名的报备要求、号码格式与发送方标识的地区差异、编码与字数上限的规则,属于各通道商与各地监管的具体规定,条文会随时间调整,实施前应当以当前签约通道商的接入文档与当地现行要求为准。涉及中国香港、中国澳门、中国台湾等地区的号码与合规要求,需单独确认,不能沿用内地同一套模板。
关于频控阈值、超时时长、重试次数、队列上限等参数,本篇给出的是推导方法与取舍逻辑,不是可直接照抄的数值。这些参数只应当在知晓自身峰值曲线、内容有效期与风险容忍度之后确定,并在上线后按自有监控数据持续校准。验证方式也很直接:在预发环境注入限流、超时、回执丢失三类故障,观察队列积压、重试放大与降级是否按设计动作,比任何纸面推演都可靠。
Copyright © 2013-2020 idc10000.net. All Rights Reserved. 一万网络 科技有限公司 版权所有 深圳市科技有限公司 粤ICP备07026347号
本网站的域名注册业务代理北京新网数码信息技术有限公司的产品