响应太慢
火了,但也慢了
我的 API 在社区里火了。
一个月后:
- 日调用量:10 万次(100 倍增长)
- 响应时间:2000ms(10 倍变慢)😰
用户开始抱怨:
- “你的 API 太慢了!”
- “有时候要等 3 秒才能返回”
- “能不能优化一下?”
每一条反馈我都看在眼里。那时候的我意识到:再不优化,这个产品就真的要死了。
调查
我决定调查问题原因。首先加了日志,记录每个请求的时间分布:
import time
import logging
@app.route('/api/weather')
def get_weather():
start = time.time()
city = request.args.get('city')
logging.info(f'Start processing city: {city}')
# 调用外部 API
api_start = time.time()
response = requests.get(f'{EXTERNAL_API_URL}?q={city}')
api_time = (time.time() - api_start) * 1000
logging.info(f'External API call: {api_time}ms')
data = response.json()
# 数据处理
process_start = time.time()
result = jsonify({
'city': data['city'],
'temperature': data['temp'],
'condition': data['weather'],
'humidity': data['humidity']
})
process_time = (time.time() - process_start) * 1000
logging.info(f'Data processing: {process_time}ms')
total_time = (time.time() - start) * 1000
logging.info(f'Total time: {total_time}ms')
return result运行一天后,我查看日志统计:
时间分布统计:
- 外部 API 调用:1800ms
- 数据处理:50ms
- 网络开销:150ms
- 总计:2000ms瓶颈找到了:外部 API 太慢!
更糟糕的消息
我查了一下外部 API 的文档,发现:
免费版限制:
- 每分钟调用次数:100 次
- 每天调用次数:10000 次计算一下当前使用情况:
- 日调用量:10 万次
- 每分钟平均:10 万 / 24 / 60 ≈ 70 次/分钟
已经接近限制了!如果用户再增长,就会被限流。
那一刻我真的慌了。
思考
我有什么办法?
方案 A:付费升级外部 API
付费版可以提高限制,但成本会增加:
- 付费版:¥1000/月,1000 次/分钟
方案 B:换一个更快的外部 API
调研其他天气 API:
- API X:¥500/月,响应 500ms
- API Y:¥800/月,响应 300ms
方案 C:减少外部 API 调用
关键问题:同一个城市的天气,多久变化一次?
我查了一下气象数据:
- 温度:每小时变化 1-3 度
- 天气状况:几小时内基本不变
- 湿度:变化缓慢
结论:同一个城市,1 小时内的天气数据基本相同!
那为什么要重复调用?
数据分析
我分析了过去 24 小时的请求日志:
请求数据分析:
- 总请求数:100,000 次
- 不同城市数:50 个
- 平均每个城市:2000 次请求/天
最热门的城市:
- 北京:15000 次
- 上海:12000 次
- 深圳:10000 次如果北京在 1 小时内被请求了 1500 次,我调用了 1500 次外部 API,但返回的是同样的数据!
这是巨大的浪费!
解决思路
在服务器上缓存结果!
缓存工作流程
用户请求北京天气
检查缓存
有北京的天气数据吗?
缓存命中
直接返回缓存数据
响应时间:20ms
缓存未命中
调用外部 API
响应时间:2000ms
存入缓存
过期时间:1 小时
返回给用户
核心思想:
- 第一次请求:调用外部 API,缓存结果
- 后续请求:直接返回缓存,不调用外部 API
- 缓存过期:1 小时后自动失效
当前技术架构
每个请求都直接调用外部 API,响应慢
客户端
用户请求 200 个开发者
应用服务
应用服务器 处理请求
缓存层 / 外部服务
外部天气 API 响应时间 2 秒
引入 Redis 缓存,大幅降低响应时间
客户端
用户请求 高并发访问
应用服务
应用服务器 处理请求
缓存层 / 外部服务
Redis 缓存 1 小时过期
外部天气 API 响应时间 2 秒
增加空值缓存,防止缓存穿透
客户端
用户请求 包含恶意请求
应用服务
应用服务器 参数校验
缓存层 / 外部服务
Redis 缓存 包含空值缓存
外部天气 API 有调用限制
使用布隆过滤器提前过滤无效请求
客户端
用户请求 包含恶意请求
应用服务
应用服务器 布隆过滤器校验
缓存层 / 外部服务
Redis 缓存 包含空值缓存
外部天气 API 有调用限制
使用互斥锁防止缓存击穿
客户端
用户请求 高并发访问
应用服务
应用服务器 互斥锁控制
缓存层 / 外部服务
Redis 缓存 热点 key 防护
外部天气 API 有调用限制
熔断器 + 降级策略应对缓存雪崩
客户端
用户请求 高并发访问
应用服务
应用服务器 熔断器 + 降级
缓存层 / 外部服务
Redis Sentinel 主从高可用
外部天气 API 有调用限制
多地域部署的高可用 API 平台
客户端
用户请求 10 万用户
应用服务
应用服务器集群 26 台,3 地域
缓存层 / 外部服务
Redis 集群 6 主 6 从
MySQL 主从 1 主 5 从
消息队列 RabbitMQ 3 台
