限流如何保护共享资源?
- 限流: 限制某类主体在一段时间内的操作频率,防止资源耗尽、滥用和不可控扩容成本;
- 限制维度: 可按用户、API Key、IP、接口或租户设置,维度决定谁与谁共享额度;
- 业务目的: 防护拒绝服务、控制数据流量和费用,也可以保障不同客户之间的公平性;
- 结果: 超额请求应得到清晰的限流响应和重试信息,而不是继续占据下游资源;
漏桶如何把突发输入整形成稳定输出?
- 漏桶: 请求进入有限队列,以固定速率处理,队列已满时拒绝新增请求;
- 收益: 下游看到更平稳的流量;
- 代价: 排队增加延迟,无法无限保留突发请求,需要明确容量与超时;
令牌桶如何允许受控突发?
- 令牌桶: 以速率 r 补充令牌,最多保留 B 个;一次请求消耗规定数量,没有足够令牌则拒绝或等待;
- 收益: 长期速率受 r 约束,同时允许消耗积攒的 B 个令牌形成短时突发;
- 实现条件: 补充、上限和消费要依据一致的时间及原子更新规则执行;
固定窗口和滑动窗口怎样计数?
| 算法 | 状态与判断 | 权衡 |
|---|
| 固定窗口 | 每个时间窗一个计数器 | 简单,但边界两侧可集中通过近两倍额度 |
| 滑动日志 | 保存时间范围内的请求时间戳 | 边界精确,但存储与处理成本高 |
| 滑动窗口计数 | 当前计数加上前窗按重叠比例加权 | 成本低于日志,但仍是近似 |
- 加权例子: 60 秒窗口已经过去 15 秒,可估算为
当前计数 + 前窗计数 × 45/60;
- 时间要求: 清理过期记录并统一窗口口径,时间漂移可能影响多节点判断;
多节点怎样共享全局额度?
- 本地计数问题: 每台机器各放行 100 次,10 台可能总共放行 1000 次,无法代表全局 100 次限制;
- 会话亲和: 同一主体固定路由到同一节点可简化状态,但扩缩容和节点失败会破坏这一前提;
- 共享存储: 使用统一计数状态可协调全局额度,但增加网络延迟和依赖故障风险;
- 分布式权衡: 要明确额度服务失效时是拒绝、局部放行还是允许有限超额,不能同时默认严格与高可用;
怎样避免计数器读改写的竞争?
- 竞争条件: 两个请求读取同一旧值后分别写回,可能丢失一次计数而放过超额请求;
- 正确边界: 计数、窗口初始化、过期和判断应放在原子操作、事务或服务端脚本的保证范围内;
- 锁的成本: 分布式锁也能序列化更新,但可能增加延迟和热点竞争;
- 原文修正: 简单调换成“先写后读”并不足够,关键是相关操作的原子性与窗口语义;