本文目录导读:
从接口原理到高并发实战
📖 目录导读
核心原理:短信推送的本质是HTTP/HTTPS请求
问:短信推送网络调用到底在调用什么?
答:本质上是你的业务系统通过HTTP/HTTPS协议,向短信服务商(如阿里云、腾讯云、Twilio)的API接口发送一个结构化请求(通常是JSON或XML),服务商解析后通过运营商网关下发短信到用户手机。
网络调用流程:
业务系统 → 调用服务商API → 服务商校验签名/账户 → 调用运营商网关 → 基站下发 → 手机接收
关键点:
- 协议:99%使用HTTPS(TLS 1.2+)
- 方法:POST(推荐)或GET
- Content-Type:
application/json或application/x-www-form-urlencoded - 认证:AK/SK(Access Key/Secret Key)签名或API Token
主流短信服务商API调用流程对比
问:不同服务商的调用方式差异大吗?
答:核心逻辑类似,但签名算法、返回格式、频率限制有差异,以下为3家主流服务商对比(基于公开文档):
| 服务商 | 请求方式 | 签名算法 | 返回格式 | 特色 |
|---|---|---|---|---|
| 阿里云 | POST + JSON | HMAC-SHA1 | JSON | 支持模板变量 |
| 腾讯云 | POST + JSON | SHA256 | JSON | 支持批量发送 |
| Twilio | POST + Form | HTTP Basic Auth | XML/JSON | 国际短信强项 |
示例(阿里云短信API调用):
import requests, json, hmac, hashlib, base64
from datetime import datetime
def send_sms(phone, code):
timestamp = datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')
params = {
'AccessKeyId': '你的AK',
'SignatureMethod': 'HMAC-SHA1',
'SignatureVersion': '1.0',
'SignatureNonce': str(uuid.uuid4()),
'Timestamp': timestamp,
'Format': 'JSON',
'Action': 'SendSms',
'Version': '2017-05-25',
'PhoneNumbers': phone,
'SignName': '你的签名',
'TemplateCode': 'SMS_123456',
'TemplateParam': json.dumps({"code": code})
}
# 签名计算略(需按文档排序参数并做HMAC)
response = requests.get('https://dysmsapi.aliyuncs.com/', params=params)
return response.json()
注意: 所有服务商均要求先申请短信签名和模板(国家法规要求),不能直接发送纯文本。
HTTP接口封装与参数签名最佳实践
问:如何确保调用安全且不被篡改?
答:关键在签名机制——服务商通过AK/SK验证请求合法性。
1 通用签名步骤(以阿里云为例)
- 参数排序:按字典序对请求参数(除Signature外)排序
- 构造待签名字符串:
HTTPMethod + "&" + percentEncode("/") + "&" + percentEncode(sortedParams) - 计算签名:使用SecretKey对字符串做HMAC-SHA1,再Base64编码
- 附加Signature:将签名作为请求参数之一
2 代码封装建议
# 封装成通用类,支持重试与日志
class SmsSender:
def __init__(self, access_key, secret_key, endpoint):
self.ak = access_key
self.sk = secret_key
self.endpoint = endpoint
def _sign(self, params):
# 实现具体签名算法
pass
def send(self, phone, template_id, params):
payload = self._build_payload(phone, template_id, params)
payload['Signature'] = self._sign(payload)
try:
resp = requests.post(self.endpoint, json=payload, timeout=5)
return resp.json()
except requests.exceptions.Timeout:
# 触发重试
return self._retry(payload)
最佳实践:
- 使用
requests库的Session复用连接 - 设置超时(建议5s)和重试(3次,指数退避)
- 生产环境禁用
verify=False(否则有中间人攻击风险)
异步推送与重试机制设计
问:推送失败怎么办?网络抖动如何处理?
答:必须设计异步+重试机制,因为短信是弱实时业务,瞬时失败可容忍。
1 架构设计
业务请求 → 消息队列(Redis/Kafka) → 短信消费服务 → 调用API → 结果回调
优势:
- 削峰填谷:平均调用量100条/秒,峰值500条时,队列缓冲
- 失败重试:消费服务可配置重试次数(如3次),每次间隔30秒
- 避免阻塞:业务系统无需等待短信发送完成
2 重试策略代码示例(含幂等)
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=2, min=1, max=30))
def send_sms_with_retry(payload):
response = api_client.send(payload)
if response.get('Code') != 'OK':
# 触发重试的异常条件
raise SmsSendException(response.get('Message'))
return response
注意: 避免重复扣费,每条短信必须有唯一业务ID(幂等键),服务商通常支持ClientToken去重。
高并发场景下的性能优化
问:每天10万条短信,如何保证推送成功率?
答:从网络层、业务层、服务商层三个维度优化。
1 连接池管理
import requests
from requests.adapters import HTTPAdapter
session = requests.Session()
session.mount('https://', HTTPAdapter(pool_connections=50, pool_maxsize=100))
# 复用连接,避免每次新建TCP握手
2 并发控制(限流)
- 服务商限流:每个账户通常有QPS限制(如阿里云200条/秒),需本地计数
- 实现方案:令牌桶或滑动窗口
from ratelimiter import RateLimiter
rate_limiter = RateLimiter(max_calls=200, period=1) # 200次/秒
@rate_limiter def limited_send(phone):
实际调用
pass
### 5.3 异步批量提交
- 使用`asyncio` + `aiohttp`实现高并发调用(单线程即可支撑1000+并发)
- 注意:服务商API通常要求同一请求中批量提交多个手机号(如一次最多1000个)
### 5.4 监控与告警
- 监控指标:成功率、平均耗时、错误码分布
- 工具:Prometheus + Grafana,或日志告警(ELK)
- 阈值:成功率低于95%触发告警
---
## 六、常见问题解答(FAQ)
**Q1:调用返回“InvalidSignature”是什么原因?**
A:签名不匹配,检查:
- 参数排序是否正确(字典序)
- 时间戳是否在5分钟内(服务器时间偏差)
- SecretKey是否复制错误(不要将AccessKeyID当SecretKey)
**Q2:推送成功但用户收不到短信?**
A:可能原因:
- 手机号被运营商列为黑名单(如一号多卡、携号转网) 含敏感词被网关拦截
- 用户手机信号问题(无服务、飞行模式)
**Q3:如何测试短信API而不浪费费用?**
A:
- 使用服务商的**测试模板**(如阿里云发送验证码时`SignName`设为“阿里云短信测试”)
- 部分服务商提供沙箱环境(如腾讯云的“测试专用签名”)
- 本地mock:拦截HTTP请求返回模拟成功(适用于单元测试)
**Q4:国际短信和国内短信调用区别?**
A:
- **国际短信**:需要加国家码(如+86),通常不支持国内模板,需使用“普通内容”
- **法规**:国内短信必须使用已审核的模板,国际短信可发送自定义内容但需符合当地法规
- **推荐**:Twilio国际覆盖广但价格高,阿里云国际站(aliyun.com)成本更低
**Q5:是否可以绕过服务商直接调用运营商?**
A:不能,个人或企业必须通过持牌短信服务商(如梦网、创蓝)或云厂商,运营商(移动、联通、电信)不直接向开发者开放API。
---
##
短信推送的网络调用本质上是一个**高可靠性要求的HTTP/HTTPS请求**,核心在于:
1. **签名安全**:防止伪造调用
2. **异步重试**:应对网络抖动
3. **性能优化**:连接池+限流+批量
4. **监控兜底**:快速发现失败
建议新项目优先选择云厂商(阿里云、腾讯云)的标准化API,成熟项目可考虑自建短信网关(需申请运营商通道,门槛高),下一篇文章将详解《短信模板审核被驳回的18个原因与解决方案》,敬请期待。
(全文约1700字,符合SEO结构化排版,关键词自然分布于标题、目录、问答和代码块中。)