做国际短信平台这几年,我最大的感触是:很多人把它当成“发短信”的API来设计,结果一上线就被各种回执格式、通道质量、计费对账问题淹没。国际短信和国内短信完全是两套逻辑,国内有运营商统一规范,通道稳定,格式统一;而国际场景下,下游是几百家运营商和通道商,协议标准虽然以SMPP为主流,但每家实现都有差异,回执格式千奇百怪,时区、语言、合规要求各不相同。这篇就围绕我实际搭过的一套国际短信平台架构,把整体设计思路、核心模块拆解、关键实现和线上踩坑记录一次说清楚,重点讲清楚“为什么这么做”,而不是只堆架构图。
1. 先把需求讲透:国际短信平台到底要解决什么问题
1.1 国际短信和国内短信的几个本质差异
在设计架构之前,得先明白国际短信平台的业务场景和痛点。一个面向企业客户的国际短信平台,核心能力是把客户的短信内容发送到海外用户的手机上,覆盖验证码、通知、营销等场景,并且能拿到完整的发送状态回执(DLR),让客户知道“这条短信到底到没到”。
这里有几个关键差异,决定了架构设计的走向。
第一是通道多样化。国际短信没有像国内三大运营商这样明确的对口接入方式,平台上可能同时存在直连海外运营商、本地SP代理、国际云通信服务商、以及一些二级批发商等几十上百条通道。每条通道的能力、覆盖国家、价格、到达率、稳定性都不一样,平台要做的是统一封装、智能调度。
第二是状态回执(DLR)格式极不统一。国内回执有标准字段,国际短信里不同通道返回的字段名、状态码含义甚至成功失败的判定逻辑都不同。有些通道只在最终失败时才回执,有些通道根本没有中间状态。这决定了我们必须做一套统一的回执归一化模块,而不是简单对接。
第三是时区和语言问题。国际平台面向全球客户,发送时间、营销内容的合规判断、内容的语言检测,都跟国内单场景不一样。比如面向中东地区,斋月期间的发送策略、模板审核规则都需要特殊处理。
第四是合规要求复杂。不同国家对短信内容、发送时间、用户授权的要求都不同,平台需要具备内容前置审核、国家码级管控、用户退订管理等机制。这部分在架构上的体现就是审核模块必须前置,且要有灵活的规则引擎。
1.2 平台的核心业务链路梳理
用一个最简单的场景来梳理链路:某出海电商客户,通过API提交了一条发往印尼用户的营销短信。这条短信从提交到最终状态回执给客户,经过了哪些环节?
- 客户调用API,携带签名、短信内容、接收号码(含国家码)、发送参数
- 平台验证鉴权、余额、内容合规性、频控规则
- 通过校验后,消息进入待发送队列
- 路由引擎根据目标国家、通道实时质量、价格、客户等级,选择最优通道
- 网关适配器把统一格式的消息包转换成对应通道的SMPP请求并下发
- 通道返回接受结果后,更新消息状态为“已提交”
- 最终用户手机收到短信,通道返回最终回执(成功、失败、超时等)
- 平台把回执归一化后,通过回调地址推送给客户,并更新计费状态
这条链路看似简单,但在高并发、多通道、多国家的真实场景下,每一环都可能出问题。架构设计的核心目标,就是在保持这条链路稳定运行的前提下,做到高吞吐、低延迟、可扩展、可追溯。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 整体架构分层设计:从API接收到状态回执的完整链路
2.1 分层思路与模块划分
我习惯把国际短信平台划分为五层:接入层、业务核心层、通道层、存储层、监控运维层。每层职责单一,层与层之间通过消息队列和标准接口解耦,这样任何一个模块升级或故障,都不会拖垮整个链路。
那为什么用分层而不是微服务到底?短信平台和普通业务系统不一样,它对链路延迟敏感,但核心流程相对固定,分层式设计在保证灵活扩展的同时,运维复杂度更低。我在实际项目中,核心链路用了分层模块化架构,辅助功能(后台管理、报表、财务系统)独立拆分,这样既不会过度设计,也不会耦合到核心链路里。
- 接入层:API网关、鉴权模块、限流模块、参数校验、内容初步过滤
- 业务核心层:消息服务(消息幂等、入库、状态流转)、路由引擎、审核模块、频控风控模块、计费模块、回调推送模块、通道管理模块
- 通道层:各种网关适配器(主要是SMPP适配器,也有HTTP API类型的通道适配器)、连接池管理、消息重发机制
- 存储层:关系型数据库(消息记录、账户、通道配置)、缓存(判重、计数、热点数据)、消息队列(削峰、解耦)、时序数据库或ES(监控数据分析、大屏查询)
- 监控运维层:质量监控大盘、告警系统、日志系统、配置中心、分布式任务调度
2.2 一条消息的完整旅程
在实际架构里,消息的流转路径是这样的(我用文字描述链路方向,方便理解顺序):
客户请求到达接入层API网关 -> 鉴权通过 -> 消息服务生成内部消息ID并进库 -> 进入待审核队列 -> 审核通过后进入路由层 -> 路由引擎选定通道 -> 消息进入对应通道的发送队列 -> 网关适配器取出消息并下发到对应的下游通道 -> 收到下游ACK后更新状态 -> 等待最终回执 -> 回执归一化处理 -> 更新消息状态和计费数据 -> 触发回调通知客户。
这条链路里有几个关键设计决策值得展开说。
消息队列贯穿全程是必须的。我刚做这套架构时,很多人问“为什么不直接同步调用”?原因很简单:不同通道下游响应时间差异巨大,有的通道100毫秒返回ACK,有的可能好几秒,而下游通道的吞吐能力远低于平台入口的请求能力。如果同步调用,一个慢通道会拖垮整个API响应,也会让应用服务器的线程池瞬间被打满。引入消息队列后,API层只要能快速落库和入队,就可以立刻返回“已受理”,真正的下发在异步链路中完成,这也变相实现了削峰填谷。实测下来,线上峰值时的API平均响应时间从原来的1.2秒优化到了80毫秒左右。
状态机和幂等设计是整个平台正确性的基础。短信平台的消息状态有:已接收、已审核、已路由、已提交下游、部分成功、最终成功、最终失败、超时等。我强烈建议为消息状态设计一套明确的状态机,所有模块只能按定义好的状态方向流转,禁止跳转错误。同时保证每一步都具备幂等性,因为消息重试、重复回执都是常态,没有幂等设计,重复回调会导致计费错误和线上事故。
3. 核心模块设计与关键实现
3.1 接入层:API网关、鉴权、幂等与频控
接入层是平台的大门,对外暴露HTTP API,核心职责包括:鉴权、参数校验、幂等处理、限流和数据采集。这个模块看似简单,但如果决策失误,后面会很难受。
鉴权方式我用的是AK/SK签名,比简单的账号密码或只传一个API Key更安全。客户提交请求时带有appId、timestamp、sign。平台用appId找到对应的SecretKey,把所有参数按字典序拼接、加盐、计算HMAC-SHA256签名,然后和客户上传的sign比对。同时校验timestamp差不能超过5分钟,防止重放攻击。签名校验是计算密集型的,这块要放在性能好的节点上,做好缓存,避免每次去数据库读密钥,线上一般用Redis缓存AppSecret,TTL设为1小时。
幂等是我非常想强调的一个点。客户的业务系统经常出现重试,网络抖动时同一笔请求可能到了平台两三次。我们要求客户在请求体中带上唯一的requestId,平台以“账号+requestId”做唯一索引。第一次请求时候落库,后续重复请求直接返回第一条的处理结果,不再重复提交。实现上,我用Redis的setnx指令原子占位,加上数据库唯一索引双保险。没有这层设计,大促期间光重复消息就够计费和对账模块崩溃的。
频控要分几个维度同时做:账号维度的每日发送总量控制、每秒QPS控制、单号码发送频率限制、同内容海量发送限制。举例来说,我们给某个客户的默认配置是每秒最多500条、单号码一天最多10条营销短信。这些计数全部走Redis的INCR和EXPIRE命令。注意,频控和限流最好在接入层就处理,不要拖到下游业务层,否则下游模块压力上来后,一旦限流不准,容易触发整链路雪崩。
3.2 路由引擎:价格、到达率、延迟的最优博弈
路由引擎是整个平台的核心大脑,负责决定一条消息走哪条通道。别小看这个模块,它直接决定平台的利润和客户到达率体验。
路由策略通常有几种常见维度:优先到达率、优先价格、优先成功率、混合打分。我实践经验是,不能只用单一维度。比如优先价格,最便宜的那条通道往往不稳定,结果客户投诉率飙升,最终流失客户,收益反而不如用价格稍高但质量稳定的通道。比较稳的做法是给通道设置多维度打分模型。我们线上的打分公式大概是:
分数 = 通道近15分钟到达率权重 + 响应延迟权重 - 价格惩罚分 - 投诉率惩罚分。
每个通道依据不同国家动态计算分数,路由时结合客户等级分流。高等级客户(如VIP,SLA要求99.5%)强制走优质通道池,普通客户走性价比通道池。同时,还需要支持手动优先级调整、通道宕机自动摘除、定时恢复等功能。
路由引擎设计还有一个容易忽略的点:国家码路由表。每个通道覆盖的国家和运营商不同,要维护一张动态的国家-通道能力表,包括是否支持该国家码、是否支持国际短信、是否支持长短信、是否支持携带中文等。这些能力差异必须在路由前置判断,避免消息发出去后才发现通道不支持该国家。
路由引擎的决策结果要同时写入消息记录和路由日志,一方面方便排查“为什么这条消息走了这条通道”,另一方面用于后续分析通道质量数据。我见过很多平台路由决策不落日志,出了问题只能靠猜,这种问题排查成本极其高昂。
3.3 通道网关层:SMPP协议适配与连接池管理
通道网关层是平台和外部通道通信的桥梁,最核心的协议是SMPP。SMPP协议本身不算复杂,核心是连接、发送命令(submit_sm)、接收回执命令(deliver_sm)、心跳(enquire_link)等,但要真正把它做稳定,有不少细节。
连接生命周期管理必须做好。每个通道要建立长连接,连接要定时发送enquire_link心跳。我在线上一开始遇到过一个问题:某些通道空闲超过一定时间会静默断开,但我们这边不知道,往断掉的连接上发送消息必然失败。后来加入严格的enquire_link机制,每隔30秒发一次心跳检测,连续3次无响应就主动重连,这个问题才解决。
SMPP连接还有个重要参数叫window大小(也称并发窗口),指在未收到响应之前,最多可以连续发送多少条消息。不同通道这个参数设置不一样,太大容易被下游丢弃,太小又发不快。我经历过一次,为了追求速度把window设置成默认值两倍,结果下游网关直接开始大批量丢弃消息,回执全是SMPP_ESME_RMSGQFUL(消息队列已满)。后面我们针对每条通道单独压测出合适的window值,部分通道还会动态调整。遇到不明通道,保守起见先设较小值,再循序渐进加大,同时观察回执的成功率和拒绝码比例。
通道层还要做好协议差异适配。虽然主流都是SMPP,但有些云通信商提供的是HTTP API,还有一些老的通道商要求走CMPP或者其他老协议。我们的做法是定义一套统一内部Message对象,字段覆盖发送源、目的地、内容、业务类型、优先级、计划时间等通用属性,然后为每种通道写一个适配器,内部实现SMPP或HTTP的转换。这样路由引擎只需要面向统一对象操作,不关心下游协议形式,新接入一条通道的改动量基本控制在适配器内部,不会影响核心链路。
通道不可用时的熔断策略也在这一层实现。我用的方案是连续错误计数配阈值:某个通道在30秒内连续失败达到50次,自动摘除该通道,不进入路由候选,同时触发告警。恢复策略有两种:定时探测,比如每2分钟发送一条测试消息,成功3次后自动恢复;或者人工确认后手动恢复。自动恢复时间窗口要设长一点,避免通道还在半死不活状态就放进来,导致消息大量失败。
3.4 状态报告(DLR)处理机制:千种格式统一归一化
国际短信平台的DLR处理是核心中的核心,也是架构复杂度的主要来源。每一条发送出去的短信,最终都必须有一个“终态”(成功或失败),并且要准确地更新到消息记录中和推送给客户。状态报告处理链路如果设计不好,会出现几个典型问题:客户一直收到“发送中”状态、回执重复推送、失败原因不准确。
DLR归一化处理模块的核心逻辑是:下游通道状态 -> 内部标准状态 -> 客户可见状态。口碑较差的通道会返回自定义状态码,比如某个拉美通道的“3”代表“已送达”,“4”反而代表“被拒绝”,另一个通道可能完全相反。所以每条通道接进来,第一件事是把它的状态码映射表维护清楚,定义成我们内部的几类:成功、失败、未投递、超时、过期、未知等。
在处理DLR时,我会建议把“短号码+长号码+发送批次消息ID”作为匹配键,而不是只用消息ID。因为部分通道在回执里不提供平台消息ID,只返回目的号码,甚至有些回执还会延迟几个小时。我遇到过极端情况,某东南亚通道默认不回执,需要在submit_sm的特定字段设置标志位才愿意返回回执,这种通道细节只能靠逐个踩坑总结。
DLR处理链路必须做到去重和幂等。同一通道的同一终态回执,可能因为网络抖动在某个重试机制下被发了两次。我们在入库时用消息ID+终态状态做了唯一约束,重复回执只会更新状态时间字段,不重新触发回调推送。另外,如果消息本身处于失败态了,再收到一条成功回执,这属于“歧义回执”,一般以先到达的终态为准,但会记录日志,留待人工核查。
因为部分通道回执延迟很高,平台必须设计一套“回执超时主动查询机制”。实际操作上,我们有一个定时任务,扫描发送后超过某个时限(默认24小时)仍未收到终态的消息,根据通道能力发起SMPP查询请求(query_sm命令),或直接标记为超时。这个扫描任务必须控制好频率和并发,避免对数据库造成压力。同时要小心:有些通道所谓查询接口也是摆设,查不到准确信息,这类通道要配置为“超时即标记未知”,不做无谓的查询重试。
3.5 计费模块:费率、对账与防超卖
计费模块对商业平台来说是命脉,设计上要保证准确且不过度开销。我的方案是:发送前冻结、回执后结算、日终对账。发送时,先冻结预估金额,等最终状态回执到达后,按实际成功/失败费率结算。冻结的目的是防止客户在消息还在飞行途中就把余额用完,导致后续消息发送失败。这部分逻辑虽然简单,但没有它,账户余额负数的纠纷会接踵而至。
国际短信的计费粒度比国内细很多。除了按条计费外,还涉及分段计费:长短信超过70个字符按多条计费(取决于编码格式,中文按67字符/条左右分段),所以计费模块必须能正确计算分段数。同时,同一平台对不同客户可能使用不同币种(美元、人民币等),费率表设计要有币种维度。我建议费率表至少包含以下字段:客户ID、国家码、通道ID、计费方向、单价、币种、生效时间、失效时间。费率调整时不要直接修改原记录,而是新增一条带新生效时间的记录,这样历史账单可以按当时的费率回溯。
风控模块和计费是联动的。国际短信平台一个常见的难题是盗刷和超卖问题,特别是有营销场景的客户。风控规则除了基础频控外,还要在链路中加入内容维度的检测。比如某些国家禁止特定类型营销内容,平台需要在审核阶段直接拦截,否则不仅收不到钱,还会导致通道被封,影响其他客户使用。另外,单号码强度监测也很重要,一些恶意请求会集中打向某个号段,这也要在风控层提前发现。
4. 关键机制与数据架构:稳定性和扩展性的保障
4.1 消息队列选型与削峰实践
消息队列贯穿整条链路,选型上我对比过RabbitMQ和Kafka。最终核心链路用的Kafka,辅助业务用RabbitMQ。原因很简单:Kafka的吞吐量高、分区有序、消息堆积能力强;而RabbitMQ更适合复杂路由和延迟队列。在短信平台的场景下,峰值流量是平时的数十倍(比如电商大促),Kafka能轻松扛住千万级消息堆积,配合消费者动态扩缩容,是整个链路抗峰值的基础。
使用Kafka时,分区策略要设计好。我建议按客户ID或消息ID取模分区分发。这里有个经验教训:不要把不同优先级消息混在同一个Topic和分区里,因为同一分区的消费顺序是固定的,一旦营销类大批量消息占满了消费者线程,验证码类高优先级消息也会被堵在后面。我们的做法是拆分成高优先级Topic(验证码、通知类)和普通Topic(营销类),消费者分别部署,数量上高优先级消费者更多些,验证码类消息的端到端延迟能稳定控制在几百毫秒。
还有一点,Kafka消费者要做手动提交offset,配合处理完成后再提交。虽然这样会在重启后出现部分重复消费,但重复消费可以通过消费者内的幂等逻辑解决,比丢失消息的风险小得多。丢消息在短信平台是不可接受的,短信丢了就真的丢了,无法补救。
4.2 数据库设计:分库分表与冷热数据分离
短信平台的数据特征是“写入多、更新多、查询条件多样”。每一条消息至少经历一次插入和多次状态更新,消息表的数据量增长非常快。一天千万级消息,一年就是几十亿条,单表根本扛不住,必须提前设计分库分表。
我的方案是:消息表按消息ID哈希分16个库,每个库再按时间按月分表。而查询侧(如客户后台检索发送记录)大多按客户ID和时间范围搜索,所以我也建了一张“客户-消息索引表”按客户ID分库,核心字段是客户ID、消息ID、发送时间、状态,查询时先定位客户库,再定位时间分段,再去消息主表拉取详情。这样既保证写入分散,又让客户维度的查询不扫全库。
冷热数据分离也很重要。终端回执通常在48小时以内到达,超过这个时间还没终态的消息很少。所以我把消息表定义热数据窗(当前月和上个月),一旦数据超过90天,通过定时任务归档到冷存储(比如分表的归档库或者大数据平台)。业务侧默认只查最近30天,需要历史数据走异步归档任务生成报表。这样热库的数据量始终可控,索引效率不会因为历史数据膨胀而下降。
Redis在架构里的角色是:频控计数、幂等判重、通道质量统计的实时计算、以及小体积热数据的缓存。这里有一个设计要点:决不要把消息内容真正存在Redis里(除非为防止重复提交做短暂的处理结果缓存),消息的权威数据只在MySQL中,Redis只做加速。否则Redis一旦回源重建,消息状态缺失或内容丢失,会造成严重事故。
4.3 降级容灾与多机房部署
架构要考虑极端情况下的可用性。我的原则是:核心链路必须无单点,所有中间件必须高可用部署;同时设计降级预案,宁可功能减配,也不能整体不可用。
Redis和Kafka都采用集群模式部署,ZooKeeper用于Kafka协调,MySQL用主从加半同步复制。如果机房整体故障,则通过DNS或路由层切流到备机房。实际操作上,做到应用双机房部署相对容易,但数据库跨机房同步和消息队列的数据一致性更复杂。我的经验是:短信平台在极端灾难场景下可以接受少量消息丢失(比如最后几秒内的消息),但绝不能接受双写冲突和消息错乱。所以跨机房灾备我采用的是消息队列双集群、异步跨机房同步的模式,业务写主集群,同一份消息通过MirrorMaker等方式同步到备集群。发生故障切换时,备集群的消息可能会少最后一点数据,通过日志记录和人工补发机制兜底。
降级预案要定期演练。我最担心的是缓存故障和队列故障,所以预案重点演练这两个场景:
- 缓存故障:Redis不可用时,频控模块直接用MySQL计数兜底(虽然性能差一些,但还能工作);路由引擎从配置中心读取静态通道配置,停止动态调整,保证消息还能发。
- 队列故障:Kafka不可用时,接入层直接把消息写入本地文件并记录日志,后端启动一个消费者定时扫文件重新入队。这种方案牺牲了一部分实时性,但不会丢消息。
监控告警系统这块要能回答三个问题:当前消息积压多少?通道15分钟到达率是多少?哪个客户在大量报错?我用的技术栈是Prometheus + Alertmanager + Grafana,自定义埋点收集以下指标:API请求量和响应耗时、各队列积压量、各通道发送量、成功率、回执平均时延、各DLR状态码占比等。告警阈值要经过一段时间的观察校准,避免“狼来了”导致告警疲劳。
4.4 通道质量管理:动态调权与问题通道淘汰
通道质量直接决定客户满意度。我每周都会做通道质量复盘,核心看几个指标:到达率、提交成功率、回执延迟、投诉率、状态码异常比例。到达率统计的难点在于要以客户实际回执为准,而不是通道自称的到达率。我们用平台统一口径:最终成功回执数/提交数。如果某个通道最近15分钟到达率低于某个阈值(比如低于90%),路由自动降权;连续30分钟低于阈值,自动摘除并告警。
通道调权是动态的,要考虑时间维度。比如某些国家的通道在特定时间段拥堵,那么凌晨2点到4点的窗口里,路由会自动偏向备用通道。这些调整全部交给路由引擎处理,不需要人工干预。但通道的全局参数(比如价格、协议类型、基础优先级)仍然由后台人工管理,这就在快速响应和人工审计之间取了平衡。
5. 线上常见问题与排查实录
5.1 高频故障场景与排查方法
故障排查是日常工作中最耗时的部分,我整理了几个高频问题,可直接对照排查。
| 现象 | 常见原因 | 排查方法 |
|---|---|---|
| 大促期间消息积压,API响应变慢 | 入口限流阈值过低或消费者线程不足 | 检查Kafka积压量和消费者Lag,扩容消费者实例;检查API层限流配置是否被触发 |
| 某个通道到达率突然暴跌 | 下游通道故障、路由权重异常、号码被屏蔽 | 到质量监控大屏查看该通道近15分钟成功率,对比其他通道;检查路由日志确认消息是否集中走了该通道 |
| 消息重复收到成功回执 | DLR去重逻辑不完善,或通道重发回执 | 检查消息终态是否被幂等约束拦截;核查DLR去重表唯一键是否生效 |
| 客户投诉收不到短信,但平台显示成功 | 部分通道回执虚假成功 | 联系通道方核实实际下发了delta,必要时做主动外呼/短信探测;将该通道从VIP客户路由池剔除 |
| 消息发出后长时间没有状态 | 通道未配置回执或回执超时时间过长 | 检查通道是否需要在submit_sm中设置回执请求标志;缩短超时扫描间隔,触发主动查询 |
| 队列消费者重启后大量重复处理 | 没有手动提交offset导致重读旧消息 | 确认消费者逻辑具备幂等性,手动提交offset改为处理后提交,同时把重复处理作为常规防御 |
这些场景里,能提前防御的尽量防御,不能防御的要保证“发现快、定位快”。所以日志链路的建设一定要重视。我们给每条消息分配一个traceId,从接入层开始贯穿到最终回执,所有模块的日志都要带traceId打印。排查问题时,只需一个traceId就能把这条消息的完整生命周期拉出来,效率非常高。
5.2 我踩过的两个印象深刻的坑
第一个坑是SMPP submit_sm的长短信处理。国际短信在GSM 7-bit编码下,超过160字符需要分片,中文(UCS-2编码)超过67个字符也要分片。我早期做的时候,有一个通道没有正确处理分片重组,客户发的验证码超过一定长度后,用户手机显示成了两条断开的短信,而且第二条内容还是乱码。排查到最后发现是适配器在发送分片短信时,没有正确设置名为UDHI的字段标志位,导致接收方无法把分片组装成完整的一条长短信。这个问题非常隐蔽,而且只在部分手机上复现。解决方案是封装一个统一的长短信分片工具,按照目标编码类型计算分片大小,设置正确的UDHI和分片序号,并且对每片独立记录消息ID用于回执匹配。
第二个坑是回调推送的重试风暴。系统一开始客户回调推送失败后,采取的是指数退避重试策略。看起来没什么问题,但当某条通道整体故障,导致大量消息最终失败时,回调队列里的失败消息瞬间爆了,重试风暴把回调服务器打到宕机,结果失败回调也没推出去。后面我做了两层保护:一层是回调推送采用独立的消费线程池,不与主链路共享资源;另一层是给回调失败的消息设置“最大重试次数”,超过次数只记录死信日志,人工定期处理,不再无限重试。这样即使通道故障,回调服务最多积压一段时间,不会被打崩。
5.3 复盘小技巧:质量问题定位三把斧
每次遇到线上问题,我习惯按“时间线、链路线、数据线”三条线并行排查。
时间线:根据告警时间倒推,看各模块在这个时间点前后的日志、监控指标、通道状态,快速确定异常起始点。链路线:根据traceId,把消息从接入到回执全链路日志串起来,定位具体卡在哪个环节。数据线:核对消息表、计费流水、路由日志、通道上报数据的差异,很多问题实际是对账不一致,最终都归结为数据不一致。
这套组合拳下来,大部分问题都能在一个小时内定位到根因。当然,前提是平时日志记录做得规范,埋点数据完整。所以我会强调一件看起来“不紧急”的事:从第一天起,就把日志规范、监控埋点、traceId贯穿作为开发标准的一部分,而不是上线后才补。这个习惯,能让后续排查问题的效率翻倍。
做国际短信平台,说到底不是把某个功能做得多花哨,而是把链路每个环节的细节抠到位。协议适配的一处疏漏、幂等设计的一次妥协、回执归一化的一个映射错误,最终都会变成线上事故。架构设计能解决80%的问题,剩下20%靠的是日常的监控、复盘和持续优化。希望这篇设计拆解能帮你少走一些我走过的弯路。
