ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

短消息中心业务功能全解析:SMPP接入、重试与话单稽核

短消息中心业务功能全解析:SMPP接入、重试与话单稽核 简介这是一份关于短消息中心业务功能的技术培训PPT课件面向通信网络运维、开发及相关学习者系统讲解SMS Center在移动网络中的核心作用。内容从短消息提交、转发、优先级与有效期管理讲起逐项说明重发机制、状态报告、用户鉴权、汉字短消息支持、虚拟短消息中心及多种调度方式还涉及节日模式、网络短消息、长短消息转发、多目的地发送、网关与报表统计等扩展功能适合作为理解短信业务架构及其处理逻辑的入门或复习材料。资源包共1个文件为PPTX演示文稿大小约444KB章节结构清晰便于按模块浏览和摘录重点。目前已有77人学习通过流程说明和功能要点速览可帮助读者建立短消息中心业务全景并掌握高负荷场景下的性能优化与多中心组网思路。1. 短消息中心业务功能为什么说它不是“发短信”三个字能概括的“发短信”这个词用户看着只有三个字短消息中心背后要处理的事却远不止三个字。一次上行短信的接收、下行短信的转发、状态报告的生成与回送、长短信的分片重组、闪信的即时呈现、黑名单拦截都在短消息中心业务功能的范畴里。很多运维同学第一次上手时习惯把这里当成一个比普通 HTTP 服务更能扛的消息盒子结果一上线就被回执错位、消息重复、短信号码显示异常搞得焦头烂额。这篇笔记的定位很直接从短消息中心的业务功能拆解开始一路讲到 SMPP 接入参数、重试策略、故障排查和话单稽核。新手能跟着把流程走通熟手可以重点看参数边界和踩坑记录。2. 拆解短消息中心业务功能从一次 MO 到状态回执的完整闭环2.1 先说清楚短消息中心在整条链路上的位置短消息中心一般缩写成 SMSC它不是独立封闭的系统而是夹在移动网络和业务系统之间的一个“存储转发”节点。移动网络侧它通过 No.7 信令网与 MSC/VLR、HLR 交互业务侧它对外暴露 SMPP、SGIP、CMPP 这类协议接口让 SP、企业应用可以在线提交下行短信或接收用户上行短信。很多人把短信网关和短消息中心当成一回事实际上业务功能边界并不相同短信网关偏重协议适配和路由转发短消息中心则真正承担了消息存储、调度重试和状态回执的生成。如果只是做一个小规模的通知下发应用也许连接的是短信网关而不是短消息中心一旦网关后端的短消息中心出现积压、重复、回执丢失业务侧看到的还是“短信发不出去”。所以理解短消息中心业务功能首先要建立两条链路的印象一条是用户手机到短消息中心的 MO 上行链路另一条是短消息中心到用户手机的 MT 下行链路而状态报告始终作为下行链路的一个分支存在。后面排查故障本质上就是在查这两条链路里的哪个环节吞了消息。2.2 一次 MO 短信在短消息中心内部走了哪几个环节用户发送一条短信比如回复“1”到一个特服号码消息先到 MSCMSC 根据号段归属把它提交到短消息中心。短消息中心拿到这条 MO 消息后并不是直接转给业务系统而是先做五件事第一检查用户是否在黑名单黑名单直接丢弃并产生拦截话单第二做内容合规检查按本地策略过滤违规内容或超长内容第三解析被叫号码判断是点对点短信还是业务短信号码这决定后续路由第四对短消息进行分片重组与编码转换比如把 GSM 7-bit 编码转成 UCS2第五生成 MO 话单并写入话单库然后把消息经由 SMPP 等接口推送给业务侧。在这一连串动作里最容易出问题的其实是“编码转换”。很多业务系统的上行内容到短消息中心时变成乱码问题常常不是网络丢包而是短消息中心在 GSM 7-bit、8-bit、UCS2 之间转换时data_coding 字段没按协议要求映射。我处理过一类奇怪故障用户发来的中文短信业务系统收到的前半段正常后半段变成问号。后来定位发现短消息中心把一条包含 emoji 的短信按 UCS2 解析却在拆分时把第二个分片误标成了 GSM 7-bit解码端拿到错误编码后只能显示出问号。这种问题在业务功能联调阶段很难暴露往往要等到跑量之后才集中爆发。MO 链路还有一个常被忽略的细节短消息中心要不要向 MSC 返回确认。短消息中心接收 MO 并成功落库后需要向 MSC 返回接收成功的响应MSC 才不会再发起重试。如果业务系统的 SMPP 接收接口响应很慢短消息中心的 MO 处理线程被拖住反过来会影响它对 MSC 的确认超时。结果就是同一个用户的同一条上行短信MSC 重发了好几次短消息中心也重复落库了几次最终业务侧看到的是“用户刷屏”。2.3 MT 短信与状态报告业务功能里最容易“回执错位”的一环MT 下行流程从业务系统提交短信开始。业务侧通过 SMPP submit_sm 把消息发给短消息中心短消息中心做鉴权、路由、费用控制处理然后按 HLR 查询结果选择合适的 MSC 投递。投递成功或者失败MSC 会返回一个投递状态短消息中心据此生成一条状态报告再通过 deliver_sm 回给业务侧。这里的“回执错位”是日常运维里最常见的痛点之一业务系统认为发送成功用户却没收到或者用户收到了话单里却显示失败。错位的根源在于状态报告没有和原始提交消息建立强关联。SMPP 协议里状态报告通过 message_id 与 submit_sm 关联而 message_id 的生成规则各厂商实现并不统一。一些短消息中心在多节点部署后提交时分配到节点 A状态报告却从节点 B 异步返回如果节点 B 没有同步消息上下文回执里的 message_id 就可能对不上。另一个常见错位场景是手动重发业务系统超时后人工重发相同内容短消息中心把两条消息当成两个独立任务用户收到重复短信但业务侧只看到一次提交返回。要避免这类问题落地时需要在短消息中心与业务侧之间约定一套完整的关联字段。我的习惯是要求业务侧在 submit_sm 里自带业务消息 ID例如写在 SMPP 可选参数 message_id 或私有 TLV 中短消息中心生成状态报告时必须原样返回这个 ID。这样业务系统收到状态报告后就可以用自己的业务 ID 和短消息中心的消息 ID 做关联而不是只知道“短信网关那边有个 ID”。如果你维护的是短消息中心自身建议从架构上把状态报告和原始提交放进同一分区或同一消费组尽量让回执按提交顺序返回否则乱序会造成误判。2.4 把功能清单翻译成系统模块一张职责表把 MO、MT 两条链路讲完后可以整理出一张功能模块清单。这张表不是给你看 PPT 目录而是后续排障和开发排期时用来对职责的。我一般会把短消息中心业务功能分成九个模块模块主要职责典型故障出口接入协议处理SMPP/SGIP/CMPP 协议编解码、连接管理连接被重置、收发超时鉴权与黑白名单主叫/被叫号段鉴权黑名单拦截批量拦截误伤编码转换与分片7-bit/UCS2 转换长短信分组乱码、分片丢失路由寻址按号段、HLR/PRN 结果决定 MSC 地址路由数据过期存储转发消息落库、定时下发、队列调度积压、丢失重试调度失败重试策略、退避时间、最大次数风暴式重试状态报告投递结果映射、状态报告生成状态错位、缺失话单计费各类话单生成、批价接口话单不平运维监控话单量、队列深度、响应时延告警淹没或漏报这张表对应的是一个标准短消息中心的职责切分。落实到具体项目时有的系统把“鉴权与黑白名单”放在更上游的短信网关有的把“话单计费”拆到独立计费域这都不影响你拿这张表做功能核对。核对的方法很简单把短消息中心每个对外接口、每张数据库表、每个后台任务分别归到这个表里找不到归属的功能就要警惕是不是历史遗留的“黑匣子”。如果让我重新搭一个短消息中心我会把每一条消息的落库字段设计成至少包含消息 ID、业务系统 ID、主叫、被叫、消息类型、优先级、提交时间、状态、重试次数、节点号。这样排障时只需要一条 SQL 就能看到消息全生命周期。很多老系统把状态字段设计成两位数字并冠以一堆自定义含义最后排障全靠猜这是最要命的。3. 让业务功能可落地SMPP 接入流程与一页纸参数表3.1 为什么落地短消息中心业务功能要先谈 SMPP短消息中心对外接口里SMPP 是事实标准几乎所有主流短消息中心和短信网关都支持。SGIP/CMPP 主要在国内某些区域使用但从业务功能落地的角度它们要处理的参数问题大概率是共通的。SMPP 最核心的能力就是三件事建立一个可靠的长连接会话、提交短信、接收短信和状态报告。用 SMPP 走通一次收发等于把短消息中心的核心链路轮了一圈。很多业务系统接入时喜欢让短消息中心按某种“内部接口”对接理由是“效率高”但我不太建议上来就这么干。标准协议的好处是两侧都有现成工具出了问题还能开包抓取 PDU 对照协议文档。自定义接口一旦出了问题两边各执一词排障只能靠猜。SMPP 会话通常分三种模式transmitter只发、receiver只收、transceiver收发一体。业务系统接入下行发送时多数短消息中心建议用 transceiver 模式这样上下行和状态报告可以在同一个连接上处理省去维护两个连接的负担。但要注意有些短消息中心的许可证按连接数计费如果业务系统节点多transceiver 模式的连接数上限可能比分别建立 transmitter 和 receiver 更早触顶。接入前先问清楚短消息中心的连接数限制这是血泪经验。3.2 最小可用的 SMPP 收发脚本从连接到提交 MO下面用 Python 的 smpplib 示例跑通一个最小收发闭环。代码不依赖具体短消息中心厂商只要对方开放 SMPP 端口即可import smpplib import time # 短消息中心侧配置按实际环境修改 HOST 192.0.2.10 PORT 3720 SYSTEM_ID smpp_client PASSWORD your_pass def on_deliver_sm(pdu): # 收到状态报告或上行短信时都会进来 print(deliver_sm, msg_id:, pdu.receipted_message_id) print(stat:, pdu.message_state) if pdu.esm_class 0x04: print(short_message:, pdu.short_message) client smpplib.client.Client(HOST, PORT) client.set_message_received_handler(on_deliver_sm) client.connect() client.bind_transceiver(system_idSYSTEM_ID, passwordPASSWORD) # 构造一条下行文本短信 params { source_addr_ton: 1, source_addr_npi: 1, source_addr: 10086, dest_addr_ton: 1, dest_addr_npi: 1, destination_addr: 13800138000, short_message: hello, data_coding: 0, } client.submit_sm(**params) # 循环读取响应让回调有机会被触发 try: while True: client.read_poll() time.sleep(1) except KeyboardInterrupt: client.unbind() client.disconnect()这段脚本的逻辑分四步先建立 TCP 连接并绑定为 transceiver 会话然后构造 submit_sm 请求提交一条下行短信接着进入轮询循环持续读取短消息中心推过来的 deliver_sm最后收到键盘中断时主动解绑并断开连接。参数里 source_addr_ton 和 source_addr_npi 分别代表源地址类型和编号计划填 1 表示国际号码填 0 表示未知真实短信中心对接时一般按对方要求填大多数情况下短消息中心不校验源地址类型但会校验源地址号码是否有权限发送。dest_addr_ton 和 dest_addr_npi 同理目标号码前缀带国家码时填 1不带时可能需要填 0。这里最容易踩的坑是 read_poll 的循环间隔。间隔太短CPU 空转间隔太长短消息中心会认为连接不活跃先发来 enquire_link 探活长时间没有响应就主动断开。我的经验是本地联调时 sleep(1) 够用生产环境接入最好由短消息中心侧指定轮询间隔或直接使用异步回调模式。还要注意 submit_sm 的响应里会返回短消息中心的 message_id这个 ID 必须和后续状态报告里的 receipted_message_id 对上对不上就是回执错位上一章已经说过。3.3 参数怎么定绑定时长、收发窗口、超时阈值的一页纸接入短消息中心的时候业务系统关注最多的参数往往不是短信内容本身而是协议连接和推送方式。我一般把参数分成三层连接层、消息层、业务层。连接层参数解决“连接能不能活”消息层参数解决“这条短信以什么编码、什么优先级发出去”业务层参数解决“要不要回执、多久算超时”。下面是我习惯整理的一页纸参数对照表参数含义常见取值备注bind_timeout连接绑定后的空闲超时300 秒左右超时后短消息中心发 enquire_link 探活enquire_link_interval客户端主动探活间隔30~60 秒短于服务端空闲超时即可window_size并发未确认的 submit_sm 数量16~64超过后要等待响应再继续发request_timeoutsubmit_sm 的响应等待时间5~10 秒太长会拖累业务侧线程registered_delivery是否需要状态报告1 表示需要成功和失败回执不需要回执时填 0data_coding短信内容编码0 为 GSM 7-bit8 为 UCS2中文必须用 8 或 UTF-8 映射priority_flag优先级0~30 最低3 最高慎用 3validity_period消息在短消息中心的有效期1~24 小时常见过期后丢弃并生成失败话单replace_if_present_flag同 message_id 是否覆盖旧消息0 不覆盖1 覆盖抄送场景慎用这组参数里最容易翻车的是 window_size。业务系统一次性提交几千条短信时如果 window_size 设置过小发送速率会被响应往返时间卡住设置过大短消息中心的接收队列会被瞬间打满反过来触发它的流控。比较稳妥的做法是先用 16 起步观察短消息中心侧的平均响应时间和队列深度再逐步上调到 32、64。validity_period 也值得注意很多业务系统把它当成普通超时时间填了“3 天”甚至更长。对通知类短信来说有效期越长重试窗口越大用户体验反而越差用户手机已经关机一整天第二天开机收到一条昨天的验证码业务上毫无意义。3.4 长短信、闪信和优先级在 SMPP 里的真实字段短消息中心业务功能里长短信、闪信、优先级这三类功能最容易被当成“简单需求”但在协议字段上各有各的坑。长短信的拆分不是简单把字符串按 70 字一段切SMPP 里要做用户数据头UDH处理。短消息中心负责拆分时submit_sm 的 esm_class 要设置 UDHI 标志同时把每条分片打上 sar_msg_ref_num、sar_total_segments、sar_segment_seqnum 三件套。业务侧如果自己先拆好了再提交短消息中心可能不会自动重组而是原样把多分片发给手机手机侧能否正确合并取决于手机实现这就容易产生“收到三片但读不出来”的投诉。闪信在 SMPP 里一般通过 data_coding 和相关的消息级别参数配合实现。最常用的是把消息的 priority_flag 设为 1并配合短消息中心管理台的“闪信号段”配置。有些厂商把闪信映射成特定 data_coding 值例如 0xF0 加特殊标记。我的观点是闪信这类功能不要试图跨厂商统一参数先看短消息中心支持哪些字段再固定成自己的配置文件。优先级则比很多人想象的危险把大量营销短信的 priority_flag 设为 3会让普通验证码短信排队时间变长。优先级的本质是队列插队插队越多系统整体的公平性越差。生产环境里优先级 3 应该只留给最高级别的告警短码不允许业务方自行指定。4. 高并发与重试策略短消息中心最容易翻车的三个地方4.1 业务功能设计时最容易忽视的“消息积压”模型短消息中心和普通 HTTP 服务最大的区别并不是吞吐量而是它天然带有“存储转发”的异步语义。业务系统把短信提交给短消息中心短消息中心返回 accept并不代表短信已经到用户手机只代表它把消息放进了内部队列。后面能不能发出去取决于 MSC 的响应、HLR 查询、用户手机状态等一系列外部因素。很多人把短消息中心的“已接收”当成“已送达”这是后续一系列误判的起点。我习惯把短消息中心的内部队列看成生产-消费模型生产速度是协议接入层接收 submit_sm 的速率消费速度是重试调度模块从队列取消息并提交到 MSC 的速率。正常场景下消费速度大于生产速度队列很浅一旦 MSC 侧出现批量超时消费速度降到接近 0队列深度会在几十秒内涨到几十万。这时如果重试调度没有做背压就会形成恶性循环队列越深重试任务越多重试又进一步挤压正常的发送任务。避免积压要从两个方向下手。方向一是限流在协议接入层对每个业务系统设置每分钟最大提交条数超过后直接返回错误或走备用队列。方向二是削峰把重试调度设计成“按号段分桶”的独立消费者每个桶有独立队列和独立告警这样即使其中一个号段的 MSC 出问题也不至于把整个短消息中心拖垮。我见过最短命的配置是全网共用一个队列一个号段的小区短信涌入后验证码通道被堵了几个小时最后只能重启清理队列。这种场景下无论参数怎么调架构上不分桶都救不回来。4.2 重试与退避从固定间隔到指数退避的参数实验重试策略是短消息中心业务功能里最考验经验的部分。固定间隔重试实现简单但有一个明显的副作用大量失败消息在同一时刻集中重试形成重试风暴。特别是一次营销活动下发几十万条短信时如果失败比例按固定 5 分钟重试那么每隔 5 分钟系统就要面对一整波重试洪峰。这个洪峰大概率会再次击穿 MSC于是下一轮失败更多下一轮洪峰更大最终把整个短信中心打挂。比较工程化的做法是指数退避加抖动。指数退避让每一条消息的重试间隔随重试次数增长避免同步共振抖动则让间隔带上随机量避免同批次消息仍然保持步调一致。下面是一段常见的重试时间计算逻辑import random base_interval 60 # 首轮重试等待 60 秒 max_interval 3600 # 单轮等待上限 1 小时 max_retry 6 # 最多重试 6 次 def next_retry_delay(retry_count): if retry_count 0: return 0 # 指数过程2^(retry_count-1) * base delay base_interval * (2 ** (retry_count - 1)) # 基础抖动上下浮动 20% jitter random.uniform(0.8, 1.2) delay delay * jitter # 封顶 return int(min(delay, max_interval)) for retry in range(1, max_retry 1): print(第, retry, 次重试等待, next_retry_delay(retry), 秒)这段代码的核心是把重试次数映射到一个随指数增长的等待时间同时加 20% 抖动。要注意的是指数退避不是避免重复而是把重复分摊到更长的时间轴上为系统争取恢复的外部条件。重试次数上限也不能拍脑袋。短消息的有效期只有两小时时重试 6 次、最大间隔 1 小时理论窗口已经超过有效期最后几次重试即使成功消息也可能因为过期被短消息中心丢弃。所以重试参数要和上一章说的 validity_period 联动设计。我一般会把“最长重试时间”控制在有效期的一半以内给最后一次投递留出足够响应时间。4.3 事务边界与幂等断电恢复后为什么短信重了短消息中心的高并发下消息重复和消息丢失是硬币的两面。为了不丢消息系统需要把消息落库并返回确认为了不重复落库时必须建立唯一约束或幂等机制。很多自研短消息中心在存储层用消息表存所有待发送消息但没有唯一索引。短消息中心向 MSC 提交消息后如果 TCP 断连MSC 到底有没有收到是不确定的。短消息中心重发一次用户就可能收到两条一模一样的短信不重发又可能彻底丢失。解决重复一般分两层。第一层是存储去重在消息表上对“业务消息 ID 分片序号”建唯一索引重复提交直接返回已存在消息的 message_id。第二层是调度去重重试调度取任务时用数据库行锁或 Redis 的 SETNX 抢锁抢到锁的消息才允许进入发送流程。下面是一个简单的去重 SQL 示例CREATE TABLE sms_message ( id BIGINT PRIMARY KEY AUTO_INCREMENT, biz_msg_id VARCHAR(64) NOT NULL, segment_seq INT NOT NULL DEFAULT 0, msisdn VARCHAR(20) NOT NULL, content TEXT, status TINYINT NOT NULL DEFAULT 0, create_time DATETIME NOT NULL, UNIQUE KEY uk_biz_seg (biz_msg_id, segment_seq) ) ENGINEInnoDB; -- 插入时发现重复则返回已存在 INSERT INTO sms_message (biz_msg_id, segment_seq, msisdn, content, status, create_time) VALUES (B20240001, 0, 13800138000, hello, 0, NOW()) ON DUPLICATE KEY UPDATE id LAST_INSERT_ID(id);在业务提交层把业务消息 ID 和分片序号作为唯一键可以让重复提交变成无害操作。需要注意的是唯一键不能只放在业务系统侧短消息中心自身也要做同样的约束因为很多重复来自 MSC 重发和短消息中心内部重试。事务边界上我习惯把“写消息表”和“更新发送状态”放在同一个事务里状态迁移靠 SQL 条件更新而不是应用层 if-else能进一步减少并发下的状态覆盖。断电恢复后短信重复十有八九是唯一约束没建或者建得不对这是埋得最深的一个坑。4.4 容量评估先算队列深度再谈并发数高并发压测时很多人只看每秒能提交多少条 submit_sm忽略了一个更重要的问题在 MSC 响应变慢时系统能容纳多少在途消息。在途消息数等于发送速率乘以平均响应时间这就是 Little 定律。假设系统每秒提交 1000 条MSC 平均响应时间 0.5 秒那么在途消息约 500 条如果 MSC 故障导致响应时间变成 10 秒在途消息瞬间变成 10000 条全部堆积在短消息中心的内存或队列里。容量评估时我会按三档建模正常档、降级档、故障档。正常档按 1 秒内完成的发送任务估算内存占用降级档按 MSC 响应变慢 5 倍估算队列深度故障档按 MSC 完全不响应估算消息堆积峰值和落库磁盘水位。短消息中心投入生产前至少要按故障档做一次演练把消息积压到磁盘容量的 60% 以上再看系统行为。很多系统在低水位时跑得很顺高水位一上来数据库连接池先耗尽消息表的锁竞争拖死整个落库链路这说明容量模型里少算了“消息积压带来的存储压力”。场景发送速率平均响应时间在途消息量重点关注正常档1000 条/秒0.5 秒500 条内存缓冲降级档1000 条/秒5 秒5000 条队列水位故障档1000 条/秒无响应持续上涨磁盘与连接池这张表的价值不在于具体数字而在于帮你确定“该为哪种场景预留资源”。把故障档给压测通过生产环境大概率能应对绝大多数突发情况只按正常档设计一次网络抖动就可能让短消息中心从“高吞吐”变成“高延迟”。5. 短消息中心常见故障与排查清单现象、原因、解法5.1 状态报告延迟或缺失现象业务系统提交短信后短消息中心很快返回 message_id但后续状态报告长时间不来或者漏了某几条。用户手机已经收到了短信业务侧却一直标记为发送中最后超时判失败。原因通常分成两种一是该条 submit_sm 的 registered_delivery 参数没有要求回执短消息中心自然不会生成状态报告二是短消息中心的状态报告模块与提交模块在两个节点上中间通过异步队列同步队列消费慢或消息丢失回执就会延迟或缺失。解法先查消息话单看短消息中心侧这条消息最终投递结果是否存在再查状态报告队列消费位置有没有落后。如果结果存在但回执没送到业务侧抓包看 SMPP 连接上短消息中心是否真的发过 deliver_sm。我遇到过的最隐蔽原因是业务侧 SMPP 连接使用 receiver 模式但短消息中心把状态报告发到了另一个绑定模式的连接上结果在业务侧看起来就是“丢了”。5.2 下行短信被网关拒绝发送现象submit_sm 提交后短消息中心返回错误码常见的有拒绝、无效目的地址、源地址无权限。用户收到的不是短信而是系统提示“发送失败”。原因可能是源号码不合法、目标号段不在短消息中心服务范围、或者该用户被加入了黑名单。还有一种情况是高峰期间短消息中心主动流控对某些业务系统返回“限流中”业务侧没做退避反复提交被短消息中心以“重复提交”拒绝。解法把短消息中心返回的错误码原样透传给业务侧不要自己映射成统一的“失败”很多错误码差异恰恰是定位关键。然后按号段白名单核对该目标号码是否属于短消息中心可服务范围。如果错误码是流控类业务侧需要指数退避而不是立即重试。我给业务系统定的规矩是凡是短消息中心返回明确拒绝的错误码一律不做自动重试转人工排查只有超时类错误码才允许自动重试这样能避免大量无效请求打在短消息中心上。5.3 消息重复下发现象用户投诉收到两遍一模一样的短信话单里却只有一条提交记录或者状态报告显示两条回执。原因通常来自三处短消息中心内部重试造成重复提交 MSC、业务系统超时后重发相同内容、以及 MO 链路上 MSC 重复投递导致重复落库。这三处的共同点是都发生在“不确定是否成功”之后系统选择重试但没有判重。解法先从话单里找出重复消息的 message_id 和提交时间看两条记录之间的时间差。如果时间差接近业务系统超时时间那大概率是业务侧重发如果时间差接近短消息中心重试间隔并且消息表中的重试次数字段累加了那问题在短消息中心侧。两侧都要建唯一索引同时把“业务消息 ID 分片序号”作为幂等键贯穿整条链路。这个坑我踩过一次之后再也不敢让任何接口不带幂等参数上生产。5.4 时钟跳变导致调度失灵现象短消息中心的定时短信功能出现诡异行为比如短信提前几分钟下发或者重试调度突然大量触发。原因听起来有点玄学但实际很常见运行短消息中心的虚拟机时钟发生跳变NTP 校时把一个只差几毫秒的时钟拨快了几秒定时任务立即大量到期。重试调度也依赖当前时间和上次失败时间做差值时钟跳变会让差值瞬间达到重试阈值。解法短消息中心的所有节点必须启用 NTP 客户端并配置可信源同时禁止应用层直接系统时间。调度任务的时间计算尽量以数据库时间为基准不要完全相信机器内存里的时钟。如果多次出现这一类问题建议在短消息中心配置参数里加一个小缓冲区例如定时任务比设定时间提前 1 秒不触发避免边界抖动造成批处理风暴。5.5 从日志和话单定位问题的方法排查短消息中心问题时最怕的是日志分散、话单不全、两侧时间对不上。我通常先把日志规范成统一格式至少包含消息 ID、业务消息 ID、协议类型、方向、源号码、目的号码、状态、耗时、节点号。然后建一个快速检索脚本按消息 ID 拉出全部生命周期轨迹#!/bin/bash # 按消息ID短消息中心日志调用方式 ./trace_msg.sh B20240001 13800138000 msg_id$1 msisdn$2 grep -h ${msg_id} /var/log/smsc/*.log | grep ${msisdn} | sort -k4 /tmp/trace_${msg_id}.log awk {print $1, $2, $4, $5, $6, $7, $8} /tmp/trace_${msg_id}.log | head -50脚本思路很简单把消息 ID 和手机号作为两个维度的过滤条件在短消息中心全部日志文件中找出相关记录按时间排序后缩小到核心字段展示。实际使用时要特别注意日志时间精度最好统一到毫秒而且所有节点的时钟要先做同步否则跨节点追踪会因为时间错位得出完全相反的结论。后面的 awk 只是精简展示真实排障时我还会把状态报告里的 message_state 和 submit_sm 返回的 message_id 单独拎出来对比。6. 把业务功能做成可运营的能力话单稽核与用例设计6.1 话单稽核从“能发短信”到“账目清楚”短消息中心上线一段时间后业务方最关心的问题往往不是“短信能不能发出去”而是“这个月应该结算多少条”。话单稽核就是把短消息中心产生的发送话单、状态报告话单、计费系统话单三方对平。常见做法是每小时做一次总量比对每天做一次明细比对。总量核对公式是MO 话单数 上行接收数MT 话单数 下行提交数成功回执数 送达数。三者各自独立不能混着算。我发现很多“金额对不上”的争议源头都是把 MT 提交数当成了成功计费数。按成功率计费的客户必须用状态报告里的成功回执数作为结算基数而不是提交数。做话单稽核前先把这层口径定义清楚否则后面对出花来也是空转。6.2 测试用例设计覆盖消息生命周期业务功能测试不能只测“提交成功”。我会按这条规则设计用例集至少覆盖 MO 上行、MT 下行、状态报告、长短信分片、闪信、黑名单拦截、无效号段、重复提交、超时重试、重启恢复和话单生成十一类场景。其中重复提交和重启恢复是最容易发现逻辑漏洞的测试方法也很简单把短消息中心进程杀掉再同时拉起一批提交请求观察恢复后消息表和发送状态是否一致。我第一次做这种测试时发现重启后有 3 条消息状态还是“发送中”既没有重试也不会失败成了永远卡住的僵尸消息后来在启动逻辑里补了一个状态恢复任务才解决。最后说一个我自己的教训刚接触短消息中心时我总觉得把发送成功率做上去就大功告成了直到上线第二个星期财务拿着对账单找上门说账目差了八千条。我才发现短消息中心话单、网关话单和计费侧话单三个口径各算各的没有一个例行稽核任务。后来我把话单核对刷成了每小时自动跑才发现很多所谓的“玄学故障”其实就是话单先漏了。做短消息中心业务功能发出去只是起点能数清楚、能追回来才是运营层面真正值钱的部分。这个思路后来帮我避开了不少坑希望也能帮到你。本文还有配套的精品资源点击获取
返回列表