短信发送失败时应记录结构化重试日志,捕获并区分可重试(如网络超时)与不可重试异常(如参数错误),使用自定义异常分类,每次重试前后记录手机号、模板ID、重试次数、原因及下次时间等关键字段,结合数据库持久化保障可靠性。

短信发送失败时记录重试日志,核心是捕获异常、区分失败类型、控制重试逻辑,并在每次重试前或后写入结构化日志(含时间、原因、重试次数等)。不建议只在 catch 块里简单打印 stack trace,而应明确记录“这是第几次重试”“失败原因是否可重试”。
捕获可重试异常并分类处理
短信接口常见失败原因有网络超时、限流拒绝、参数错误、通道不可用等。其中超时、限流、临时性通道故障通常可重试;而手机号格式错误、签名未审核通过等属于业务错误,重试无意义。
- 用 自定义异常 区分可重试(如
SmsNetworkException)和不可重试(如SmsValidationException) - 调用短信 SDK 后,检查返回码或捕获底层异常(如
SocketTimeoutException、IOException),封装为对应自定义异常 - 避免直接 catch
Exception,防止把NullPointerException这类编程错误也当成可重试异常
在重试逻辑中嵌入日志记录点
无论用 Spring Retry、手动 for 循环,还是消息队列延迟重投,都要确保每次重试动作触发前/后写日志。重点字段包括:手机号、模板 ID、重试序号、异常简述、下一次重试时间(如有)。
在调试 Java 后端 API 故障时使用此技能,尤其适用于以下任务:分析日志、跨 Controller、Service 和 Mapper 层追踪请求、定位异常根因、检查 SQL 执行或参数流转,以及生成简洁的诊断结论与修复方案。
- 示例日志(SLF4J 格式):
logger.warn("SMS retry[{}]: to {} with template {}, cause: {}, next in {}s",
retryCount, phone, templateId, e.getMessage(), delaySeconds); - 重试次数建议从 1 开始计数(首次调用算第 0 次,第一次重试是 retryCount=1),避免混淆
- 若使用
@Retryable,可通过RetryContext获取当前重试次数,配合@Recover记录最终失败日志
日志内容需支持后续排查与监控
单纯记录“发送失败”没价值。要让运维或开发能快速判断是通道问题、配置问题,还是数据问题。
立即学习“Java免费学习笔记(深入)”;
- 必含字段:手机号(脱敏显示,如 138****1234)、模板编码、渠道名称(如 “阿里云短信”、“腾讯云 SMS”)、HTTP 状态码或 SDK 错误码、原始错误信息(截取前 200 字,防日志爆炸)
- 建议补充上下文:用户 ID(如有)、订单号、触发来源(如 “注册流程”、“支付通知”)
- 避免记录敏感内容:明文验证码、身份证号、完整 token —— 日志不是数据库
结合异步与落库提升可靠性
如果应用重启,内存中的重试状态会丢失。关键业务建议将待重试任务持久化。
- 首次发送失败后,把任务写入数据库(状态 = “待重试”,含下次执行时间、已重试次数、错误摘要)
- 单独起一个定时任务(如每分钟扫一次),捞出到期的待重试记录,执行重发并更新状态
- 每次操作数据库前后都记日志:“已入库重试任务”“开始执行重试 #3”“重试成功/彻底失败”
- 这样即使服务挂掉,日志+数据库也能还原完整轨迹,比纯内存重试更可控

















