短信发送失败后应区分错误类型:仅对网络超时、5xx等临时错误重试,参数错误须立即告警;采用指数退避(1s→2s→4s)、限重试3次,并结合回执回调与状态轮询双机制保障不丢消息。

短信发送失败后怎么自动重试才不丢消息
重试不是简单循环调用 send_sms(),必须区分失败类型:网络超时、运营商限流、参数错误要分别处理。网络类失败(如 requests.exceptions.Timeout 或 502 Bad Gateway)可立即重试;参数错误(如手机号格式错、签名未报备)重试 100 次也没用,得立刻终止并告警。
实操建议:
- 用指数退避(Exponential Backoff)控制重试间隔,例如第 1 次等 1s,第 2 次等 2s,第 3 次等 4s,最多 3 轮
- 把重试逻辑封装进独立函数,接收原始参数 + 当前重试次数,避免在业务主流程里混入 sleep
- 每次重试前检查手机号是否仍有效(比如是否已停机),避免无效重试消耗配额
- 记录每次重试的
error_code和error_msg,方便后续按错误码聚合分析
批量发送时如何避免压垮下游短信网关
直接 for 循环发 1 万条,大概率触发网关限流(返回 429 Too Many Requests 或 ErrCode:1015)。必须做流量整形,不能只靠“加个 time.sleep()”这种粗暴方式。
实操建议:
- 用并发池控制最大并发数,Python 推荐
concurrent.futures.ThreadPoolExecutor,max_workers 设为 5–10(具体看网关文档允许的 QPS) - 每批请求加唯一
batch_id,便于排查某批全失败还是部分失败 - 对单次 HTTP 请求设置明确 timeout(如
timeout=(3, 8)),防止一个卡死拖垮整批 - 提前预校验手机号格式和模板变量占位符数量,失败在发之前,不占用网关额度
怎么保证“发了但没回执”的消息不丢失
短信网关通常只返回“提交成功”,不代表对方收到。真正可靠的方式是依赖回执回调(callback)或主动轮询状态 API。但回调可能延迟、重复、丢失,轮询又增加负担——必须双保险。
实操建议:
- 发送时写入本地数据库,字段至少含:
phone、template_id、submit_time、status(初始为 'submitted')、sms_id(网关返回的唯一 ID) - 启动一个后台任务,每 5 分钟查一次
status = 'submitted'的记录,调用网关的query_sms_status()接口更新状态 - 对超过 2 小时仍是 'submitted' 的记录,标记为 'unknown' 并触发人工核查,而不是盲目重发
- 回调到达时,先校验签名和
sms_id,再更新 DB,避免重复消费
Python 里用 requests 发短信要注意哪些坑
看似简单的一行 requests.post(url, json=payload),实际在线上常因细节翻车:中文乱码、签名被 URL 编码、Header 缺失、SSL 验证失败。
实操建议:
- 显式指定
headers={'Content-Type': 'application/json; charset=utf-8'},否则某些网关会当乱码处理 - 敏感字段如
sign或timestamp必须按网关要求顺序拼接+签名,别直接用json.dumps()后哈希 - 禁用 SSL 验证(
verify=False)仅用于测试,生产必须带正确 CA 证书路径,否则requests.exceptions.SSLError会静默失败 - 捕获
requests.exceptions.RequestException而非只抓ConnectionError,覆盖 DNS、连接、读取等全部异常











