导航菜单

响应太慢

火了,但也慢了

我的 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 台

搜索