尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

短信业务流程分析:从短信中心到计费系统的全链路拆解与状态机验证

短信业务流程分析:从短信中心到计费系统的全链路拆解与状态机验证 简介这份《短信业务流程分析》PPT面向通信、嵌入式及管理信息化方向的开发与运维人员系统梳理短信业务从存储转发机制到PDU编码落地的完整链路适合需要理解短信收发原理、调试AT指令或优化短信服务系统的技术人员参考。资源包内含1个pptx文件约1.16MB以图文幻灯片形式组织内容便于按章节浏览与课堂讲解。目前已有171人学习下载。内容覆盖SMS存储转发模式与SMSC转发机制、PDU与Text两种发送模式对比、7-bit/8-bit/UCS2三种编码方式并整理ATCMGC、ATCMGF、ATCMGS、ATCSCA等常用AT指令的功能说明同时拆解PDU格式的A至M各字段构成配合“工作愉快”实例演示短信中心号码与接收号码的奇偶位交换、国际化标志添加及长度计算等编码步骤可帮助读者建立从协议原理到实际编码的完整认知为短信模块开发与排错提供参考。1. 短信业务流程分析从一条短信的旅程看系统瓶颈在哪一条短信从手机发出到对方收到中间要穿过短信中心、信令网、网关、计费系统、SP平台任何一环卡住用户看到的就是“发送失败”或者延迟几分钟才到。很多人以为短信就是“发出去就完事”但真正做过短信业务系统的人都知道这里面的流程链路比大多数HTTP接口调用复杂得多。短信业务流程分析要解决的核心问题是把这条链路上每个节点的处理逻辑、状态流转、异常分支全部拆开找到延迟、丢失、重复的根因。适合谁看做短信网关开发的、做SP业务后台的、做运营商侧接口对接的以及需要排查短信收发异常但又拿不到底层日志的运维和测试人员。下面我按实际排查问题的思路把这条链路从头到尾走一遍。2. 短信从提交到送达的完整链路拆解2.1 短信中心在链路里到底做了什么短信中心是整条链路的枢纽。手机提交短信时通过信令通道把消息发给短信中心短信中心先做鉴权——检查这个号码有没有短信权限、是否欠费、是否被列入黑名单。鉴权通过后短信中心把消息存下来返回一个提交响应给手机这时候手机界面上显示“已发送”。注意这个“已发送”只代表短信中心收到了不代表对方收到了。接下来短信中心要查路由接收方当前在哪个MSC移动交换中心下注册。如果接收方开机且在网络中短信中心通过信令把消息推给接收方所在的MSCMSC再通过基站发给手机。如果接收方关机或不在服务区短信中心会把消息存起来等接收方重新注册时再尝试投递。这个“存储转发”机制是短信可靠性的基础但也是延迟的根源——状态报告回来之前你根本不知道对方到底收没收到。常见做法是在短信中心侧配置重试策略比如首次投递失败后间隔5分钟重试最多重试3次超过后标记为失败并生成状态报告。这个重试间隔和次数直接决定了用户感知到的延迟上限。2.2 状态报告怎么回传为什么经常对不上状态报告是短信业务流程里最容易出问题的地方。短信中心投递成功后会生成一个状态报告通过信令回传给短信发起方。但实际场景中状态报告经常丢失或者延迟。原因有几个一是状态报告走的是和短信不同的信令通道可能被限流二是SP平台和短信中心之间的协议映射有问题比如SMPP协议里的deliver_sm和submit_sm_resp的message_id对不上三是跨运营商时状态报告格式不兼容。我一般会这样排查先在SP平台侧记录每条短信的message_id和提交时间然后在短信中心的日志里搜同一个message_id看状态报告是什么时候回来的、状态码是什么。如果短信中心侧显示已投递但SP侧没收到状态报告那问题就在回传通道上。如果短信中心侧压根没有投递记录那问题在提交环节。# 在短信中心日志里按message_id搜索投递记录 grep MSG_ID123456789 /var/log/smsc/smsc_delivery.log | grep -E submit|deliver|report # 输出示例 # 2024-01-15 10:23:45 submit_sm from 8613800138000 to 8613900139000 MSG_ID123456789 # 2024-01-15 10:23:47 deliver_sm to MSC_001 MSG_ID123456789 statusDELIVRD # 2024-01-15 10:23:48 report back to SP MSG_ID123456789 statusDELIVRD上面这段日志说明短信在2秒内完成了投递并回传了状态报告。如果只有第一行没有后面两行说明短信卡在了短信中心内部需要检查路由表或者接收方号码的归属MSC是否可达。2.3 用SMPP协议对接时必调的4个参数SMPP是SP平台和短信中心之间最常用的协议。对接时如果参数没调对会出现连接频繁断开、短信提交超时、状态报告收不到等问题。下面这4个参数是我踩过坑之后每次都会重点检查的。参数典型值作用调错后果enquire_link_interval30秒心跳间隔设太长会被短信中心断开设太短浪费信令submit_sm_timeout5000毫秒提交超时设太短会误判失败设太长会阻塞后续提交window_size10滑动窗口大小设太大导致短信中心限流设太小吞吐上不去max_connection2最大连接数单连接故障时没有冗余多连接要注意负载均衡# SMPP客户端连接参数配置示例 import smpplib client smpplib.client(smsc.example.com, 2775) client.set_message_sent_handler(lambda pdu: print(fSent: {pdu.sequence})) client.set_message_received_handler(lambda pdu: print(fDelivered: {pdu.sequence})) # 关键参数设置 client.connect() client.bind_transceiver( system_idsp_account, passwordsp_password, enquire_link_interval30, # 心跳间隔30秒 submit_sm_timeout5000, # 提交超时5秒 )这段代码里enquire_link_interval设成30秒是经验值大部分短信中心在60秒无心跳后会断开连接。submit_sm_timeout设5秒是因为短信中心内部处理通常在1秒内完成超过5秒基本可以判定为异常。window_size在bind_transceiver之后通过client.set_window_size(10)设置控制同时未确认的PDU数量。3. 计费与话单生成环节的实操要点3.1 短信计费触发点在哪为什么会有话单丢失短信计费通常不是在短信中心完成的而是在短信中心向计费系统发送话单之后。话单里包含发送方号码、接收方号码、短信类型、提交时间、状态等字段。计费系统根据话单生成扣费记录。问题在于短信中心和高并发场景下话单可能因为队列积压而丢失或者因为格式错误被计费系统丢弃。常见做法是短信中心侧先把话单写入本地磁盘队列再由一个独立的话单采集进程读取并发送给计费系统。这样即使计费系统暂时不可用话单也不会丢。我一般会检查三个地方短信中心的话单队列文件是否有积压、话单采集进程是否在运行、计费系统的话单入库日志是否有格式错误。# 检查话单队列积压情况 ls -lh /var/spool/smsc/cdr/ | tail -5 # 如果文件数量持续增长且时间戳很旧说明采集进程卡住了 # 检查话单采集进程 ps aux | grep cdr_collector # 如果没有输出说明进程挂了需要重启 # 检查计费系统入库日志 tail -100 /var/log/billing/cdr_import.log | grep -i error\|reject3.2 话单字段映射的3个易错点话单从短信中心到计费系统中间可能经过格式转换。我遇到过最多的问题就是字段映射错误。比如短信中心用caller表示发送方计费系统用calling_number如果映射脚本写错了计费系统就会把接收方当成发送方来扣费。还有时间格式短信中心可能用Unix时间戳计费系统要求YYYY-MM-DD HH:MM:SS转换时区没处理对就会导致话单时间偏移8小时。第三个易错点是短信类型编码。普通短信、长短信、国际短信、SP短信的计费费率不同如果类型字段映射错了要么少扣费要么多扣费。我一般会在话单采集进程里加一个校验逻辑对每条话单检查发送方号码和接收方号码是否都是有效的手机号格式时间戳是否在合理范围内短信类型是否在已知枚举值里。校验不通过的话单写入单独的错误队列人工介入处理。4. 短信业务流程分析中的避坑与排查记录4.1 状态报告延迟导致重复发送现象用户反馈收到两条一样的短信但SP平台日志显示只提交了一次。原因短信中心投递成功后回传状态报告但状态报告在信令通道上延迟了。SP平台在submit_sm_timeout时间内没收到状态报告判定为失败并触发重试于是短信中心又投递了一次。解决在SP平台侧维护一个已发送消息的message_id缓存收到状态报告后更新状态。重试前先查缓存如果该message_id已经有状态报告了就不再重试。同时把submit_sm_timeout适当调大给状态报告留出回传时间。4.2 长短信拆分后计费翻倍现象用户发送一条超过70个字符的短信话单显示扣了两次费。原因长短信在短信中心会被拆分成多条普通短信分别投递每条都会生成独立话单。如果计费系统没有做长短信合并就会按条数扣费。解决在话单里增加concat_reference和concat_total字段计费系统根据这两个字段判断是否属于同一条长短信合并后只计一次费。短信中心侧也要确保拆分后的每条短信都带上相同的concat_reference。4.3 SMPP连接被短信中心限流断开现象SP平台和短信中心之间的SMPP连接每隔几分钟就断开一次日志显示ESME_RTHROTTLED。原因window_size设得太大短时间内提交了大量短信短信中心触发限流保护主动断开连接。解决把window_size从默认的10降到5同时在提交短信的代码里加一个令牌桶限流器控制每秒提交的短信条数不超过短信中心允许的上限。具体上限需要跟短信中心侧确认一般是每秒100到500条不等。4.4 国际短信路由错误导致投递失败现象发送到境外号码的短信全部失败状态报告显示UNDELIV。原因短信中心的路由表里没有配置该国家代码的出口路由或者配置的出口网关不支持该运营商的协议。解决检查短信中心路由表确认目标国家代码有对应的路由条目。如果没有需要联系短信中心管理员添加。同时确认出口网关的协议类型SMPP、CIMD、UCP等和目标运营商的协议是否匹配。4.5 话单时间戳时区不一致现象计费系统里的话单时间比实际发送时间早了8小时导致夜间话单被算到了前一天。原因短信中心用UTC时间生成话单计费系统按本地时间解析中间没有做时区转换。解决在话单采集进程里统一把时间戳转成带时区的ISO 8601格式比如2024-01-15T10:23:4508:00。计费系统侧解析时按带时区的方式处理避免歧义。5. 用状态机模型验证短信业务流程的完整性5.1 把短信生命周期画成状态机短信从提交到最终状态可以抽象成几个状态SUBMITTED已提交到短信中心、ENROUTE正在投递、DELIVRD已投递、EXPIRED过期、DELETED已删除、UNDELIV无法投递、REJECTD被拒绝。每个状态之间的转换都有对应的触发条件。用状态机模型来验证业务流程好处是能穷举所有可能的路径发现遗漏的异常分支。我一般会用一个简单的状态转换表来检查从SUBMITTED出发可能到ENROUTE、REJECTD、EXPIRED从ENROUTE出发可能到DELIVRD、UNDELIV、EXPIRED。如果实际日志里出现了状态转换表里没有的路径比如从SUBMITTED直接到DELIVRD那说明中间某个环节的日志丢了或者状态报告被错误映射了。5.2 用脚本自动校验状态转换合法性# 短信状态转换合法性校验脚本 VALID_TRANSITIONS { SUBMITTED: [ENROUTE, REJECTD, EXPIRED], ENROUTE: [DELIVRD, UNDELIV, EXPIRED], DELIVRD: [], UNDELIV: [], EXPIRED: [], REJECTD: [], DELETED: [], } def validate_transition(prev_status, curr_status): 检查状态转换是否合法 if prev_status not in VALID_TRANSITIONS: return False, f未知的前置状态: {prev_status} if curr_status not in VALID_TRANSITIONS[prev_status]: return False, f非法转换: {prev_status} - {curr_status} return True, 合法 # 从日志里提取状态序列并逐条校验 status_sequence [SUBMITTED, ENROUTE, DELIVRD] for i in range(1, len(status_sequence)): ok, msg validate_transition(status_sequence[i-1], status_sequence[i]) if not ok: print(f第{i}步异常: {msg})这个脚本的核心逻辑是维护一张合法转换表然后对日志里的状态序列逐条检查。如果发现非法转换比如SUBMITTED直接跳到DELIVRD就需要去查中间是不是漏了ENROUTE的日志或者状态报告里的状态码映射错了。实际使用的时候我会把这个校验逻辑嵌入到日志分析管道里每天定时跑一次把异常转换的记录输出到报表里。5.3 状态机验证发现的典型问题用状态机跑一遍之后最常见的发现是EXPIRED状态被滥用。有些短信中心在投递失败后不生成UNDELIV而是直接标记为EXPIRED导致SP平台无法区分是“对方关机导致过期”还是“路由错误导致无法投递”。这两种情况的处理策略完全不同前者可以等对方开机后重试后者需要修路由。所以我在对接短信中心时会明确要求对方在状态报告里区分这两种状态码。另一个常见问题是DELETED状态。这个状态通常出现在短信被短信中心主动删除的场景比如存储满了需要清理。但如果DELETED出现在ENROUTE之后说明短信在投递过程中被删了这通常意味着短信中心内部有异常。我一般会把这个状态单独告警因为正常业务流程里不应该出现。5.4 一个实用技巧用message_id串联全链路日志最后分享一个我一直在用的技巧在短信提交时生成一个全局唯一的message_id然后把这个ID透传到短信中心、网关、计费系统的所有日志里。这样排查问题时只需要拿一个message_id去各个系统的日志里搜就能把整条链路的处理过程串起来。实现方式是在SMPP的submit_sm包里加一个可选参数message_payload或者用short_message字段的前几个字节存message_id。短信中心侧需要支持把这个ID透传到状态报告和话单里。这个做法一开始需要跟短信中心侧协调但一旦落地排查效率会提升很多。以前查一条短信为什么失败要在四五个系统之间来回翻日志现在一个grep就能定位到卡在哪一环。希望帮到你。本文还有配套的精品资源点击获取
返回列表