第一次接触京东云短信服务时,我也被各种专业术语搞得头晕。但实际用起来发现,只要掌握几个核心概念,就能快速上手。AccessKey/SecretKey相当于你的账号密码,在京东云控制台的"应用管理-概览"页面就能找到。这里特别提醒:千万别把这些信息直接写在代码里!我吃过亏,后来都改用环境变量存储。
地域ID(regionId)固定填cn-north-1就行,这是华北-北京机房。就像寄快递要写收货地址一样,这个参数告诉API你的短信要从哪个数据中心发出去。记得我第一次用的时候傻傻地改成自己城市,结果接口直接报错...
模板ID和签名ID是短信的"身份证"。模板ID对应着你事先在控制台审核通过的短信内容,比如"您的验证码是{code}"。签名ID则是显示在短信开头的【公司名称】。有次我忘记配置签名,短信直接显示【未知来源】,用户都不敢点开看。
python复制# 初始化客户端示例
from jdcloud_sdk.core.credential import Credential
from jdcloud_sdk.services.sms.client.SmsClient import SmsClient
# 强烈建议从环境变量读取密钥
import os
access_key = os.getenv('JDCLOUD_ACCESS_KEY')
secret_key = os.getenv('JDCLOUD_SECRET_KEY')
credential = Credential(access_key, secret_key)
client = SmsClient(credential)
发送接口BatchSend看着简单,但藏着不少细节。去年双十一我们系统崩溃,就是因为没处理好这几个问题:
坑一:手机号格式必须带国际区号,国内号码要写成['19924444844']。有次我直接写11位数字,接口居然返回成功,但用户根本没收到短信!后来发现京东云会把不带区号的号码默认当成国际号码处理。
坑二:模板变量要注意类型匹配。比如模板里写{code}对应数字,但传了字符串就会报错。建议先用控制台的"测试发送"功能验证,比直接调API省时间。
坑三:频率限制默认每秒50条。有次营销活动我们没做限流,瞬间触发流控,整个服务被临时封禁。后来改成异步队列+定时发送才解决。
python复制def send_verification_code(phone, code):
try:
parameters = BatchSendParameters(
regionId='cn-north-1',
templateId='你的模板ID',
signId='你的签名ID',
phoneList=[phone]
)
# 注意模板变量必须是字符串列表
parameters.setParams(params=[str(code)])
request = BatchSendRequest(parameters)
resp = client.send(request)
if resp.error:
print(f"发送失败: {resp.error.message}")
return False
print(f"短信已提交,序列号: {resp.result['sequenceNumber']}")
return True
except Exception as e:
print(f"接口异常: {str(e)}")
return False
很多开发者发完短信就不管了,等用户投诉才查问题。其实京东云提供了完整的状态报告机制:
每个发送请求都会返回sequenceNumber,相当于快递单号。通过StatusReport接口,能查到每条短信的最终状态:是否成功、运营商返回码、接收时间等。我们团队现在每天凌晨跑脚本,把前24小时所有失败记录入库分析。
更高级的玩法是用消息队列订阅状态报告。京东云支持将状态实时推送到指定HTTP接口,我们接入了企业微信机器人,遇到"黑名单号码"或"空号"就自动告警。这样能及时发现号码库质量问题,节省不少短信费用。
python复制def check_status(sequence_number):
try:
parameters = StatusReportParameters(
regionId='cn-north-1',
sequenceNumber=sequence_number
)
request = StatusReportRequest(parameters)
resp = client.send(request)
if resp.error:
print(f"查询失败: {resp.error.message}")
return None
# 典型响应结构示例
return {
'status': resp.result['status'],
'phone': resp.result['phone'],
'report_time': resp.result['reportTime']
}
except Exception as e:
print(f"查询异常: {str(e)}")
return None
大部分教程只讲发送短信,却忽略用户回复的场景。比如物流通知短信里带上"回复1确认收货",就能实现简单的业务交互。
京东云的Reply接口支持按日期查询所有用户回复。我们电商项目用这个功能做了自动售后系统:当用户回复"退货",系统自动触发工单流程。关键点是要在控制台提前配置回复回调地址,建议用HTTPS协议确保安全。
有个冷知识:回复短信的有效期默认只有72小时。有次活动我们第三天才处理回复,结果一半数据都查不到了。现在重要活动都会每小时同步一次数据。
python复制def fetch_replies(app_id, date):
try:
parameters = ReplyParameters(
regionId='cn-north-1',
appId=app_id,
dataDate=date
)
request = ReplyRequest(parameters)
resp = client.send(request)
if resp.error:
print(f"查询失败: {resp.error.message}")
return []
return [{
'phone': item['phone'],
'content': item['content'],
'reply_time': item['replyTime']
} for item in resp.result['replyContents']]
except Exception as e:
print(f"查询异常: {str(e)}")
return []
在真实业务中,直接调用原生API会遇到各种问题。经过多次踩坑,我们总结出一套企业级方案:
连接池管理:每次创建新连接会有300ms左右的延迟。我们改用单例模式维护客户端实例,配合连接池参数调整,吞吐量提升了5倍。注意要设置合理的超时时间,建议发送接口不超过3秒,查询接口不超过10秒。
重试策略:网络抖动时简单重试可能雪上加霜。我们的策略是:首次失败立即重试,第二次失败等待1秒,第三次等待3秒。配合断路器模式(比如连续5次失败就熔断10分钟),系统稳定性明显提高。
监控看板:用Grafana+Prometheus搭建了短信监控体系,关键指标包括:
python复制# 优化后的发送示例
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, max=10)
)
def robust_send(phone, code):
try:
# 使用连接池中的客户端
global sms_client
if not sms_client:
sms_client = init_client()
return send_verification_code(phone, code)
except Exception as e:
print(f"第{robust_send.retry.statistics['attempt_number']}次尝试失败")
raise e
去年我们系统被恶意调用,一晚上发了几万条验证码短信,损失惨重。后来实施了这些防护措施:
频率限制:在API网关层对每个IP/账号做限流。比如验证码接口限制同一号码60秒内只能请求1次,全天不超过10次。注意业务高峰期要适当放宽限制,我们618期间就临时调整过阈值。
内容审核:虽然模板已审核,但动态变量仍需过滤。现在我们会对所有模板参数做关键词检测,防止XSS攻击。比如有人尝试在收货人姓名里注入JavaScript代码...
敏感操作验证:修改签名/模板、查询历史记录等操作,强制要求二次验证。建议开启操作日志审计功能,我们曾通过日志发现过内部员工的违规操作。
python复制# 简单的频率限制装饰器
from functools import wraps
import time
def rate_limit(max_calls, period):
def decorator(func):
calls = []
@wraps(func)
def wrapper(*args, **kwargs):
now = time.time()
calls_in_period = [t for t in calls if t > now - period]
if len(calls_in_period) >= max_calls:
raise Exception("操作过于频繁,请稍后再试")
calls.append(now)
return func(*args, **kwargs)
return wrapper
return decorator
@rate_limit(max_calls=1, period=60)
def send_sensitive_code(phone):
# 重要操作验证码
pass
短信看着单价低,但量大起来也很惊人。我们通过这些方法每月节省40%费用:
错峰发送:运营商在凌晨时段费率更低。把非紧急通知类短信(如账单提醒)安排在0:00-6:00发送,成本能降20%。但要注意用户作息,我们曾经在凌晨给老年人发短信被投诉...
智能路由:根据号码归属地选择运营商。比如移动号码走移动通道,联通号码走联通通道。通过京东云的运营商匹配功能,到达率提升了15%。
长短信优化:超过70个字符会按多条计费。我们对模板做了分词优化,把"尊敬的VIP会员先生/女士"简化为"尊敬的客户",单条短信字数从85降到62,年省十几万。
python复制# 费用计算工具函数
def calculate_cost(phone_list, content):
base_price = 0.045 # 条单价
long_msg_price = 0.09 # 长短信价格
# 计算长短信条数
msg_length = len(content)
if msg_length <= 70:
per_msg_count = 1
else:
per_msg_count = (msg_length - 1) // 67 + 1 # 长短信每67字算一条
total_cost = len(phone_list) * per_msg_count * (
long_msg_price if per_msg_count > 1 else base_price
)
return {
'message_count': len(phone_list),
'segment_per_message': per_msg_count,
'total_segments': len(phone_list) * per_msg_count,
'estimated_cost': round(total_cost, 2)
}