跳到主要内容

限流

限流如何保护共享资源?​

  • 限流: 限制某类主体在一段时间内的操作频率,防止资源耗尽、滥用和不可控扩容成本;
  • 限制维度: 可按用户、API Key、IP、接口或租户设置,维度决定谁与谁共享额度;
  • 业务目的: 防护拒绝服务、控制数据流量和费用,也可以保障不同客户之间的公平性;
  • 结果: 超额请求应得到清晰的限流响应和重试信息,而不是继续占据下游资源;

漏桶如何把突发输入整形成稳定输出?​

  • 漏桶: 请求进入有限队列,以固定速率处理,队列已满时拒绝新增请求;
  • 收益: 下游看到更平稳的流量;
  • 代价: 排队增加延迟,无法无限保留突发请求,需要明确容量与超时;

令牌桶如何允许受控突发?​

  • 令牌桶: 以速率 r 补充令牌,最多保留 B 个;一次请求消耗规定数量,没有足够令牌则拒绝或等待;
  • 收益: 长期速率受 r 约束,同时允许消耗积攒的 B 个令牌形成短时突发;
  • 实现条件: 补充、上限和消费要依据一致的时间及原子更新规则执行;

固定窗口和滑动窗口怎样计数?​

算法状态与判断权衡
固定窗口每个时间窗一个计数器简单,但边界两侧可集中通过近两倍额度
滑动日志保存时间范围内的请求时间戳边界精确,但存储与处理成本高
滑动窗口计数当前计数加上前窗按重叠比例加权成本低于日志,但仍是近似
  • 加权例子: 60 秒窗口已经过去 15 秒,可估算为 当前计数 + 前窗计数 × 45/60;
  • 时间要求: 清理过期记录并统一窗口口径,时间漂移可能影响多节点判断;

多节点怎样共享全局额度?​

  • 本地计数问题: 每台机器各放行 100 次,10 台可能总共放行 1000 次,无法代表全局 100 次限制;
  • 会话亲和: 同一主体固定路由到同一节点可简化状态,但扩缩容和节点失败会破坏这一前提;
  • 共享存储: 使用统一计数状态可协调全局额度,但增加网络延迟和依赖故障风险;
  • 分布式权衡: 要明确额度服务失效时是拒绝、局部放行还是允许有限超额,不能同时默认严格与高可用;

怎样避免计数器读改写的竞争?​

  • 竞争条件: 两个请求读取同一旧值后分别写回,可能丢失一次计数而放过超额请求;
  • 正确边界: 计数、窗口初始化、过期和判断应放在原子操作、事务或服务端脚本的保证范围内;
  • 锁的成本: 分布式锁也能序列化更新,但可能增加延迟和热点竞争;
  • 原文修正: 简单调换成“先写后读”并不足够,关键是相关操作的原子性与窗口语义;