1. 从一条短信的送达说起:为什么还要折腾MAS
做过企业级消息推送的人大概都有过这种经历:系统里明明显示"发送成功",客户却打电话过来说压根没收到验证码。你去查日志,接口返回码是0,状态是DELIVRD,一切看起来都完美无缺,但短信就是没到用户手机上。这种时候,问题往往不在你的代码,而在于你对整个短信下发链路的理解还停留在"调个接口就完事"的层面。
中国移动MAS(Mobile Agent Server,移动代理服务器)就是这条链路上绕不开的一环。简单说,它是中国移动面向集团客户提供的一个接入网关,企业系统通过它把短信提交给移动的网络,再由移动完成最终的投递。它解决的是一件事:让企业能够以相对标准化的方式,批量、稳定地把短信送到移动用户的手机上。适合谁来参考这篇内容?如果你正在做企业短信通知、验证码下发、会员营销触达,或者你是个后端开发、运维、技术负责人,需要把短信能力接进自己的业务系统,那这篇东西应该能帮你少走不少弯路。
我见过太多团队在这个环节上栽跟头,不是因为技术有多难,而是因为信息太散、文档太旧、坑太隐蔽。运营商侧的接口文档往往写得像天书,字段含义含糊,错误码表不全,测试环境跟生产环境行为不一致。所以我想把自己在实际对接中积累的东西系统性地梳理一遍,从协议选型到状态回执,从签名报备到限流策略,尽量把那些"文档里不会写但实际会碰到"的细节讲清楚。
需要先说明一点:MAS本身是一个比较宽泛的概念,不同省份、不同接入方式(比如CMPP、SMPP、HTTP网关)在细节上会有差异。下面讲的内容是基于常见的实践总结,具体参数和流程还是要以你实际拿到的接入文档为准。但底层逻辑和踩坑点是相通的,理解了这些,你再看任何一份具体的接口文档都会快很多。
2. MAS短信的三种接入姿势:CMPP、SMPP与HTTP网关怎么选
2.1 长连接协议CMPP:高并发场景下的主力选择
CMPP是中国移动自己定义的一套短信网关协议,全称是中国移动点对点协议。它基于TCP长连接,采用二进制报文格式,是MAS对接里最"正统"也最常用的方式。为什么高并发场景都选它?因为长连接省去了每次发送都建连的开销,而且协议本身支持异步提交和批量下发,吞吐量能做得比较高。
CMPP的交互模型是客户端主动连接网关,然后通过一系列操作码(Command_ID)来完成登录、提交短信、接收回执、接收上行等动作。核心的几个操作码你得记住:CMPP_CONNECT用于登录鉴权,CMPP_SUBMIT用于提交短信,CMPP_DELIVER用于网关向客户端投递状态报告或上行短信,CMPP_ACTIVE_TEST是心跳保活。登录的时候需要提供源地址(Source_Addr)、时间戳和认证码,认证码的计算方式通常是MD5(Source_Addr + 共享密钥 + 时间戳),这个细节如果算错了,登录会一直返回鉴权失败,而且错误信息往往很模糊。
我实际对接时踩过的一个坑是时间戳格式。CMPP要求的时间戳是MMDDHHMMSS格式的十进制数字,不是Unix时间戳,也不是带年份的完整时间。当时我按Unix时间戳传过去,网关直接拒绝,排查了半天才发现是格式问题。另外心跳间隔也有讲究,太短浪费资源,太长容易被网关判定为死连接而断开,一般建议30到60秒之间,具体看网关侧的配置。
2.2 SMPP协议:国际标准下的另一种可能
SMPP是国际通用的短消息点对点协议,很多第三方短信平台和部分移动网关也支持。它的报文结构跟CMPP类似但字段定义不同,比如系统ID、密码、ESME地址这些概念。如果你的系统本身就要对接多家运营商或国际通道,用SMPP可能更统一,因为它是标准协议,各家实现差异相对小一些。
但要注意,国内移动的MAS接入,很多省份默认只开放CMPP,SMPP需要单独申请,而且支持的版本(3.3、3.4)和可选参数(TLV)也不一定完整。所以选型的时候别想当然,先确认你拿到的接入方式到底是什么。我见过有团队照着SMPP文档开发了一周,结果对接时发现对方只给CMPP,白白浪费了时间。
2.3 HTTP网关:快速接入的轻量方案
有些场景下,你不需要长连接的高吞吐,只是偶尔发几条通知,那HTTP网关就是最省事的选择。它本质上是一个RESTful接口,你POST一个请求过去,带上账号、密码、手机号、内容等参数,网关同步返回提交结果。开发成本极低,用curl都能测。
但HTTP网关的短板也很明显:一是性能有限,不适合大批量发送;二是状态回执的获取方式不同,通常需要你提供一个回调地址,或者主动去轮询查询接口;三是部分省份的HTTP网关只对特定行业开放。所以它适合中小规模、对实时性要求不极端的场景,比如内部告警通知、低频的会员提醒。
下面这张表可以帮你快速判断该选哪种方式:
| 接入方式 | 协议类型 | 适用场景 | 开发复杂度 | 吞吐能力 | 回执获取 |
|---|---|---|---|---|---|
| CMPP | TCP长连接/二进制 | 高并发、大批量 | 中高 | 高 | 长连接推送 |
| SMPP | TCP长连接/二进制 | 多通道统一对接 | 中高 | 高 | 长连接推送 |
| HTTP网关 | HTTP短连接 | 低频、快速接入 | 低 | 低 | 回调或轮询 |
选型的核心逻辑其实就一句话:看你的业务量和实时性要求。日均几千条以内、对延迟不敏感,HTTP足够;日均几十万上百万条、要求秒级触达,老老实实上CMPP长连接。
3. 签名、模板与报备:短信能不能发出去的前置关卡
3.1 短信签名不是随便起的名字
很多人以为短信签名就是短信开头那个【某某公司】的标识,随便填一个就行。实际上签名是需要提前报备审核的,而且审核规则相当严格。签名内容必须能明确标识发送主体,通常要求是企业简称、品牌名或产品名,不能是泛泛的词汇,也不能包含违规内容。
报备签名时需要提供企业资质证明,签名内容要和资质主体有关联。比如你公司叫"某某科技",签名报"某某科技"一般没问题,但如果你报一个跟公司名毫无关系的词,大概率会被驳回。签名的长度也有限制,一般不超过一定字符数,太长会被截断或拒绝。我建议签名尽量简短且唯一,既能通过审核,也方便用户一眼识别来源。
还有一个容易被忽略的点:签名报备是有生效时间的,不是提交了马上就能用。通常需要几个工作日审核,所以项目排期的时候一定要把这个时间预留出来,别等到上线前一天才发现签名还没批下来。
3.2 模板报备:内容合规的第一道防线
除了签名,短信内容本身也需要按模板报备。模板就是你预先定义好的短信文案格式,其中可变部分用变量占位。比如验证码模板可能是"您的验证码是{code},{minutes}分钟内有效"。报备通过后,你发送时只能替换变量,不能随意改动固定文案。
为什么要这么严格?因为运营商要对短信内容负责,防止垃圾短信和诈骗信息。所以模板里的用词也有讲究,不能有诱导性、欺诈性表述,不能包含敏感词。变量部分通常也有格式限制,比如验证码只能是数字,长度固定。
实际操作中,模板审核被驳回是家常便饭。常见的驳回原因包括:文案里有"退订"以外的营销诱导词、变量位置不合理导致语义不通、缺少必要的业务标识等。我的经验是,模板文案尽量朴实、信息明确,别玩文字游戏。另外,同一个业务场景最好多报几个备用模板,万一主模板被驳回或需要调整,不至于卡住整个项目。
3.3 报备流程中的时间成本与沟通技巧
报备这件事,技术含量不高但极其耗时间。从提交资质、填写模板、等待审核到最终生效,整个周期可能从几天到一两周不等。而且不同省份、不同行业的审核尺度还不一样。有些行业(比如金融、政务)审核更严,需要的材料更多。
我的建议是:第一,项目启动阶段就把报备作为独立任务排进计划,别当成开发完成后的收尾工作;第二,跟对接的客户经理保持沟通,他们通常能告诉你当前审核的松紧程度和常见驳回原因;第三,所有报备材料一次准备齐全,反复补充材料是最浪费时间的。还有个小技巧,如果业务紧急,可以问一下是否有加急通道,部分省份对特定类型的业务是支持的。
4. 从提交到送达:一条短信的完整生命周期与状态回执
4.1 提交成功不等于送达成功
这是新手最容易误解的地方。当你通过CMPP或HTTP提交一条短信,网关返回的"成功"只代表它接收了你的请求,并不代表短信已经到了用户手机。真正的送达确认要靠状态报告(Status Report),也就是回执。
短信的完整生命周期大致是这样的:你的系统提交短信到MAS网关,网关返回提交结果(CMPP里是CMPP_SUBMIT_RESP,HTTP里是同步响应);然后网关把短信交给移动的核心网,核心网尝试投递给用户手机;投递完成后,核心网生成状态报告回传给网关,网关再通过CMPP_DELIVER或回调的方式推送给你的系统。只有收到状态报告,你才知道这条短信最终是成功了还是失败了。
所以你的系统设计里,必须有一个地方来接收和处理状态报告,并且把回执和原始发送记录关联起来。关联的钥匙通常是msg_id,也就是提交时网关返回的消息标识。这个msg_id一定要存好,否则回执来了你都不知道是哪条短信的。
4.2 读懂状态报告里的stat字段
状态报告里最关键的信息是stat字段,它表示最终的投递状态。最常见的几个值你需要认识:
- DELIVRD:投递成功,短信已送达用户手机。
- UNDELIV:投递失败,通常是用户关机、停机、空号等原因。
- EXPIRED:短信过期,一般是因为用户在有效期内没有开机或不在服务区。
- REJECTD:被拒绝,可能是内容违规或用户设置了拒收。
- UNKNOWN:未知状态,网关也无法确定结果。
看到UNDELIV和EXPIRED不要慌,这不一定是你的问题,很多时候是用户侧的原因。但如果是大面积出现UNDELIV,那就要排查了,可能是号码格式有问题,或者你发的号段本身有问题。REJECTD则要重点关注内容合规性,看看是不是触发了什么过滤规则。
4.3 回执丢失与补偿查询机制
状态报告不是百分之百可靠的。网络抖动、连接断开、网关重启都可能导致回执丢失。如果你的业务强依赖送达结果(比如验证码必须确认送达),那就需要设计补偿机制。
常见的做法是:发送后把记录标记为"待确认",启动一个定时任务,超过一定时间还没收到回执的,主动去查询状态。CMPP协议本身没有标准的查询操作码,但很多网关提供了扩展接口或者HTTP查询接口。如果实在查不到,就只能根据超时时间做兜底处理,比如超过5分钟没回执就标记为"未知",业务上做相应处理(比如允许用户重新获取验证码)。
这里有个经验:不要无限期等待回执。设置一个合理的超时窗口,比如10到30分钟,超过就归档。否则你的待确认表会越来越大,查询和关联的性能都会受影响。
5. 限流、频控与内容过滤:那些让短信发不出去的隐形规则
5.1 运营商的限流策略你绕不过去
就算你的系统性能再强,运营商侧的限流也是硬约束。MAS网关通常会对每个账号设置发送速率上限,比如每秒多少条、每天多少条。超过这个限制,请求会被直接拒绝或丢弃。
这个限流值不是固定的,跟你的账号等级、业务类型、省份都有关系。有些账号默认限流很低,需要申请提额。提额通常要说明业务场景和预估量级,审核通过后才能调整。所以如果你的业务有突发流量(比如大促时的营销短信),一定要提前评估并申请足够的配额,别等到流量来了才发现被限流。
应对限流的技术手段主要是队列和削峰。把要发的短信先入队,然后按网关允许的速率匀速消费。这样既能保证不超限,又能在流量高峰时平滑处理。队列可以用Redis、RabbitMQ或者数据库表来实现,看你的技术栈和可靠性要求。
5.2 频控:同一个号码不能无限骚扰
除了整体限流,运营商还有针对单个手机号的频控。同一个号码在短时间内收到太多短信,会被判定为骚扰,后续短信可能被拦截。这个频控规则各运营商不同,但一般是一分钟内不超过若干条、一天内不超过若干条。
从业务角度,你也应该自己做频控。比如验证码短信,同一个号码60秒内只能请求一次,这是行业惯例,既能防刷,也能避免用户反感。营销短信更要注意,同一个用户一天收到多条营销短信,投诉率会飙升,而投诉率高了会影响你的通道质量甚至被关停。
实现频控一般用Redis的计数器加过期时间,key用手机号加业务类型,每次发送前检查计数。这个逻辑不复杂,但一定要做,而且要做得细,区分不同业务类型的频控策略。
5.3 内容过滤:为什么你的短信被拦截了
内容过滤是另一个隐形杀手。运营商和网关会对短信内容做实时过滤,命中敏感词或规则的短信会被拦截或标记。常见的触发点包括:涉及金钱、中奖、贷款等敏感词汇,包含网址链接(尤其是短链),文案格式像诈骗短信等。
规避的方法不是去猜敏感词库然后绕开,那样既不可靠也不合规。正确的做法是让内容本身合规:营销短信要有明确的退订方式,文案要真实不夸大,链接要谨慎使用。如果是验证码类短信,内容尽量简洁,只包含必要的验证码和有效期说明,不要夹带其他信息。
我遇到过一种情况:同样的模板,平时发都正常,某段时间突然大面积失败。排查后发现是那段时间运营商加强了某类内容的过滤,而我们的文案里有个词恰好命中了新规则。这种时候只能改文案重新报备。所以内容合规是个持续的事,不是报备通过就一劳永逸。
6. 实战中的高频故障与排查思路
6.1 登录失败:认证码、时间戳与IP白名单
CMPP登录失败是最常见的入门级问题。排查顺序建议这样:先确认认证码计算是否正确,MD5的拼接顺序和密钥有没有搞错;再确认时间戳格式是不是MMDDHHMMSS;然后检查你的服务器IP有没有加入网关的白名单。很多网关要求源IP必须预先报备,否则连接直接被拒。
如果这些都对了还是登录失败,那就看返回的错误码。CMPP_CONNECT_RESP里有个Status字段,不同的值对应不同的原因。常见的有:认证失败、超过连接数限制、账号被禁用等。把错误码和文档对照一下,基本能定位。
6.2 提交成功但收不到:从msg_id追到状态报告
这种情况就是我开头说的"假成功"。你的提交返回了msg_id,说明网关收了,但用户没收到。这时候你要做的是拿msg_id去查状态报告。如果状态报告显示UNDELIV,看原因;如果压根没有状态报告,那可能是回执链路出了问题,或者短信还在处理中。
还有一种可能是号码问题。比如你发的号码是异网号码(联通或电信),而你的通道只支持移动,那自然发不出去。或者号码格式不对,缺少国家码或多了前缀。这些都要在提交前做校验。
6.3 长连接断开的自动重连与状态恢复
CMPP是长连接,网络波动或网关维护都会导致连接断开。你的客户端必须实现自动重连,而且重连后要重新登录、重新建立心跳。更重要的是,重连期间未收到回执的短信要能恢复追踪,不能因为断连就丢了。
我的做法是:连接状态用一个独立的监控线程来管理,断开后按指数退避策略重连(比如1秒、2秒、4秒、8秒,直到上限)。重连成功后,把断连期间积压的待发送消息重新提交,同时检查待确认的回执记录,必要时触发补偿查询。这套机制听起来复杂,但用状态机来管理其实很清晰。
6.4 回执乱序与重复:幂等处理的重要性
状态报告可能乱序到达,也可能重复推送。比如你先收到一条短信的DELIVRD,过一会儿又收到同一条的DELIVRD。如果你的系统没有幂等处理,就会重复更新状态,甚至触发重复的业务逻辑。
解决办法是给每条回执做唯一标识,通常用msg_id加状态报告的序列号或时间戳。处理回执前先检查这个标识是否已经处理过,处理过就丢弃。数据库层面可以用唯一索引来兜底,插入冲突就忽略。这个细节看似小,但在高并发下能避免很多诡异的问题。
7. 通道质量监控:怎么知道你的短信通道是不是在变差
7.1 送达率不是唯一指标
很多人只看送达率,觉得送达率高就万事大吉。其实送达率要结合场景看。验证码短信的送达率通常很高,因为用户正在操作,手机大概率在线。营销短信的送达率天然就低一些,因为很多用户关机或不在服务区。所以你不能拿营销短信的送达率去跟验证码比。
除了送达率,还要关注提交成功率、回执到达率、平均送达时长这些指标。提交成功率低说明你的请求有问题;回执到达率低说明回执链路不可靠;平均送达时长突然变长说明通道可能拥堵了。
7.2 建立分省分运营商的监控看板
短信通道的质量是分区域的。同一个通道,在A省可能很好,在B省可能就很差。所以监控要细化到省份和运营商。这样才能在某个区域质量下降时快速发现,并及时跟运营商沟通或切换通道。
看板上至少要能看到:各区域的提交量、成功率、送达率、平均延迟,以及趋势变化。如果某个区域的成功率突然掉了几个百分点,那就是预警信号。这种监控不需要多复杂,用现有的监控系统加几个自定义指标就能做。
7.3 通道切换与降级策略
如果你的业务对接了多个通道(比如主通道加备用通道),那就要设计切换策略。主通道质量下降或不可用时,自动切到备用通道。切换的触发条件可以是成功率低于阈值、延迟超过阈值或连接断开。
但切换不是无脑切,要考虑成本。不同通道的单价可能不同,备用通道通常更贵。所以策略应该是:主通道正常时优先用主通道,异常时才切备用,主通道恢复后切回来。这个逻辑用配置中心加健康检查就能实现,关键是切换和回切都要有明确的判断条件,避免频繁抖动。
8. 一些用血泪换来的实操心得
先说一个关于测试环境的坑。很多省份的MAS测试环境和生产环境是两套完全独立的系统,账号、密钥、网关地址都不一样,甚至协议版本都可能有差异。你在测试环境调通了,不代表生产环境就能跑。所以拿到生产账号后,一定要做一轮完整的回归测试,别直接上量。
再说一个关于字符编码的坑。短信内容涉及中文时,编码方式直接影响计费条数和能否正常显示。CMPP里通常用GBK或UCS2编码,用错了要么乱码要么长度计算错误。一条短信70个字符的限制是按编码后的字节数算的,中文用UCS2编码时每个字占2字节,所以实际能放的字数更少。长短信的拆分和拼接也有讲究,需要正确设置TP_udhi和分片序号,否则用户收到的可能是乱序的几段。
最后说一个关于日志的坑。短信链路的排查高度依赖日志,但很多团队的日志记得不全。我的建议是:提交请求、网关响应、状态报告、业务处理结果,这四个环节都要有日志,而且要用同一个trace_id串起来。这样出问题时,你拿一个手机号或msg_id就能把整条链路串出来。日志里别记完整的手机号和短信内容(涉及隐私),做脱敏处理,但msg_id、时间、状态这些关键字段一定要全。
还有一点,跟运营商侧的沟通要有耐心也要有方法。他们的问题响应可能没那么快,所以你要把问题描述清楚:什么时间、什么号码、什么现象、你这边看到的msg_id和状态是什么。信息给全了,他们排查起来快,你的问题解决得也快。别只说"短信发不出去",那样来回沟通的成本太高了。
这套东西我前后折腾了好几年,从最开始连认证码都算不对,到后来能自己搭监控、做容灾,中间踩的坑基本都写在这里了。短信这个事,技术门槛不算高,但细节特别多,而且很多细节是文档里不会写的。希望这些经验能让你在对接MAS的时候少熬几个夜。