Java 项目对接短信接口,网上能搜到不少示例代码,复制过来改改参数就能发出第一条短信。可示例代码一般只管“发出去”,真放到生产环境,超时怎么办、重复请求怎么办、回执怎么收,这些都得自己补。
这篇不针对某一家平台,只讲 Java 端对接短信接口时比较通用的工程思路。具体参数名和签名规则,以你用的平台文档为准。
先把短信接口封装成独立的服务类
不要在业务代码里直接拼 HTTP 请求。建议单独写一个短信服务类,对外只暴露几个方法,比如发验证码、发通知、批量发送。业务方只传号码、模板编号和变量,鉴权、签名、请求、解析这些都收在服务类内部。
这样做的好处很实在:以后换平台或者加一个备用通道,只改这一个类,业务代码一行不动。
签名计算:照着文档一步步对
多数平台的签名逻辑大同小异:把参数按规则排序,拼成字符串,加上密钥,再做一次摘要运算。写成示意代码大概是这样:
params 按 key 字典序排序
拼接 str = key1=value1&key2=value2...
sign = 摘要算法(str + 密钥)
请求时带上 sign 和时间戳
Java 里容易出错的地方有三个:字符编码没统一用 UTF-8,中文变量一算就错;时间戳单位搞混了秒和毫秒;URL 编码在签名前做还是签名后做,和文档不一致。排查时把文档的示例参数代进去,算出来和文档结果一致,说明你的实现没问题。
密钥别写死在代码里,放配置中心或者环境变量,日志里也不要打印出来。
超时和重试要分情况
调用短信接口一定要设连接超时和读取超时,不设的话,平台一卡,你的线程池就跟着堵死。
重试要谨慎。失败分两种:一种是明确失败,比如参数错误、模板未审核、余额不足,这种重试没意义,直接记录并报警;另一种是结果不确定,比如读取超时,请求可能已经到了平台,只是响应没回来。第二种如果直接重试,用户就可能收到两条一样的短信。
所以重试前最好先做幂等处理。
幂等:给每条短信一个业务唯一号
做法是发送前在本地生成一个唯一的业务流水号,和号码、内容一起落库,状态记为“发送中”。如果平台支持传自定义流水号,就一起传过去。
重试时先查这个流水号的状态,已经成功的不再发;平台支持按流水号去重的,重复请求也只会发一次。验证码场景还可以加一层:同一号码在有效期内重复请求,直接返回之前那条的结果,而不是再生成新的。这样配合业务层的频次限制和图形验证,也能挡住一部分刷短信接口的行为。
回调:验签、去重、异步处理
短信最终有没有送达,要看平台推回来的状态回执。Java 端写回调接口时注意:
- 先验签,确认请求确实来自平台,不验签的回调接口谁都能伪造;
- 按流水号去重,平台可能会重复推送同一条状态;
- 收到就先返回成功响应,再把状态丢进队列异步更新,别在回调里做耗时操作,否则平台判定超时会反复重推。
如果长时间收不到回执,可以写一个定时任务,主动调用平台的查询接口补拉状态。
日志和监控别省
每次调用短信接口,都记一条日志:流水号、号码(中间几位打码)、模板编号、平台返回码、耗时。出问题时,客服说“客户没收到验证码”,开发能在一分钟内查到这条短信走到了哪一步,是没发出去、平台拒了,还是运营商那边没送达。
监控可以从简单的做起:失败数突然变多、平均耗时明显变长、余额低于某个值,这几种情况触发报警就够用。我们见过最冤的一次故障,是账户余额用完了,验证码全部发不出去,用户注册卡了整整一个上午,而系统里其实早就有报错,只是没人看。
如果业务对验证码依赖很重,还可以接一条备用通道,主通道连续失败时自动切换。前面封装好的服务类,这时候就派上用场了。
结论
Java 对接短信接口,核心不在“能发出去”,而在几个工程细节:服务类封装方便替换通道,签名计算对着文档逐项核对,超时重试区分明确失败和结果不确定,用业务流水号做幂等,回调先验签再异步入库。这几块做扎实,线上基本不会出现重复发送和状态丢失。蔚岚科技的短信群发服务提供完整的接口文档和技术支持,对接中遇到问题可以打 18720919319 联系我们。
常见问题
Java对接短信接口用什么HTTP客户端比较好?
常用的几种 HTTP 客户端都可以,关键是用连接池并设置好超时,不要每次请求都新建连接。
发送短信要不要做成异步?
建议异步。业务流程里只负责把发送任务写进队列,由单独的消费者调用短信接口,避免平台响应慢拖累主流程。
签名一直报错怎么排查?
把参与签名的原始字符串打印出来,和文档示例逐字比对,重点看排序、编码和时间戳。
回调接口需要放在公网吗?
需要平台能访问到。可以单独做一个轻量的回调服务放在公网,内部再转给业务系统。
批量发送时要注意什么?
控制好单次请求的号码数量和并发,按平台限制分批提交,并且只发给已授权或有业务往来的用户。