服务限流实战

5 min

设想有这样一个场景,我在我的 UGC 平台接入了一个评论机器人,用户在其他用户的评论区可以通过 @robot 来触发回复。由于我的机器人回复是使用第三方 API,如 OpenRouter,来实现个性化 LLM 回复的,因此这里涉及到限流的问题。

瓶颈

image.png
image.png
  • 第一个是用户请求层面的,我们不能无限制地纵容用户一直调用机器人评论服务。
      1. 在功能层面,我们可以限制用户单位时间内的调用次数,比如使用 Redis 记录每分钟调用次数不能超过 5 次。
      1. 在技术层面,若已经在功能层面进行限制了,但是用户基数非常大,实际上仍旧有很大规模的用户在调用这个服务,我们可以使用 MQ 或者令牌桶来进行限流。
  • 第二个是 LLM 服务端的流量限制。
    • LLM 服务端如 OpenRouter,对单个用户的请求会有 RPM/TPM 限制,这属于外部瓶颈。
    • 可以使用 MQ + 令牌桶做流量整形,以及加上熔断策略。
  • 第三个是 LLM 返回信息给我们,但是我们写入评论服务流量太大,单个线程可能也来不及处理,因此我们可以使用线程池来并行写入,同时也可以使用 MQ 来削峰填谷。

限流和熔断

首先明确这两个概念:

限流:是限制系统的请求频率或内部某些功能的执行频率,防止突发的流量激增导致整个系统不可用,常见的 限流算法 有滑动窗口、漏桶、令牌桶等。

熔断:当调用外部服务、数据库或微服务时,如果连续出现失败、超时或响应过慢,熔断机制会“断开电路”,暂时中止调用该依赖。在熔断开启期间,系统直接返回错误或降级结果,不再继续发请求。等一段“冷却时间”后,再少量放行测试请求;如果恢复正常,熔断自动关闭。

熔断特性

  • 慢调用熔断
  • 异常比例熔断
  • 半开状态试探

针对上一节提到的三个瓶颈,针对用户请求层面上的,我们主要使用限流策略;针对调用下游 LLM 服务的,我们主要使用熔断策略;针对回写数据库这里属于内部 IO,可以使用 MQ 做异步来进行削峰填谷。

实战

计划

第一部分分为业务和技术两类限流。

  • 业务限流:
    • 限制每人每分钟请求次数不超过 5 次
    • 限制每人每天请求次数不超过 20 次
      • key 设计
    • 每次请求需消耗一个金币,金币不足不能被请求
  • 技术限流:
    • 使用令牌桶算法,限制总体 QPS 不超过 100

第二部分为调用 LLM 外部服务,主要设计熔断策略。

  • 要求
    • 如果返回大于 60s 就熔断
    • 如果异常比例大于 50% 就熔断
    • 熔断时间过后,拿到真实用户请求,类似令牌桶,给个许可去请求下游

第三部分为回写 DB。

  • 引入 Kafka 即可

中间件选用

  • 当然我们可以自己实现以上的限流与熔断策略,但是有成熟的中间件给我们使用。
  • Resilience4j 和 alibaba/Sentinel 都有以上的功能,最终选取了 Sentinel。
  • 原因
    • Sentinel 可以配置原生控制台,可视化调整。
    • Sentinel 在针对 LLM 这类限流时,有 Pacing 匀速排队的功能,可以让突发的请求以均匀的速度发给下游。
    • 同时 Sentinel 还有令牌桶模式,符合我们的各种流量处理需求。