目录
- 固定窗口算法
- 滑动窗口算法
- 漏桶算法
- 令牌桶算法
- Sentinel限流例子
固定窗口算法
固定窗口算法的实现相对简单。系统维护一个计数器,每当请求到来时,计数器加一。当时间窗口结束时,计数器清零。如果在一个时间窗口内请求数量超过了预设的阈值,则拒绝后续的请求
public class FixedWindowRateLimiter {private AtomicInteger counter = new AtomicInteger(0);private volatile long lastRequestTime;private long windowUnit = 1000L; // 假设时间窗口为1000毫秒private int threshold = 10; // 阈值为10public synchronized boolean fixedWindowsTryAcquire() {long currentTime = System.currentTimeMillis();if (currentTime - lastRequestTime > windowUnit) {counter.set(0);lastRequestTime = currentTime;}if (counter.get() < threshold) {counter.incrementAndGet();return true;}return false;}
}
滑动窗口算法
- 动态窗口划分
将时间切分为更细粒度的子窗口(如1秒分为10个100ms窗口),每个新请求触发窗口滑动,仅统计最近完整时间周期内的请求总量(如最近1秒的请求数)。
- 精准流量控制
通过维护滑动窗口内的请求时间戳队列(如使用Redis的ZSET结构),实时计算有效请求数量,避免固定窗口算法在时间边界处允许双倍流量突发的缺陷
漏桶算法
桶有固定容量,水以固定速率从桶中流出
漏桶模式中的消费处理总是能以恒定的速度进行,可以很好的保护自身系统不被突如其来的流量冲垮;但是这也是漏桶模式的缺点,假设 QPS 为 2,同时 2 个请求进来,2 个请求并不能同时进行处理响应,因为每 1s / 2= 500ms 只能处理一个请求
令牌桶算法
令牌桶算法的原理是系统会以一个恒定的速度往桶里放入令牌,而如果请求需要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。从原理上看,令牌桶算法和漏桶算法是相反的,一个“进水”,一个是“漏水”。
如 Guava RateLimiter
系统服务作为生产者,按照指定频率向桶(容器)中添加令牌,如 QPS 为 2,每 500ms 向桶中添加一个令牌,如果桶中令牌数量达到阈值,则不再添加。
请求执行作为消费者,每个请求都需要去桶中拿取一个令牌,取到令牌则继续执行;如果桶中无令牌可取,就触发拒绝策略,可以是超时等待,也可以是直接拒绝本次请求,由此达到限流目的。
Sentinel限流例子
- 定义资源
- 通过代码定义资源
- 通过注解定义资源
@RequestMapping("/getuser")
public String getUser() {try (Entry entry = SphU.entry("getuser")) {// 被保护逻辑return "User";} catch (Exception e) {// 限流之后的业务逻辑return "被限流了";}
}
- 定义限流规则
private static void initFlowRules() {List<FlowRule> rules = new ArrayList<>();FlowRule rule = new FlowRule();rule.setResource("resourceName"); // 资源名称rule.setGrade(RuleConstant.FLOW_GRADE_QPS); // 根据 QPS 限流rule.setCount(1); // QPS 阈值【每秒只允许通过一个请求】rule.setStrategy(RuleConstant.STRATEGY_DIRECT); // 调用关系限流策略【非必须设置】rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT); // 流控效果【非必须设置】rule.setClusterMode(false); // 是否集群限流【非必须设置,默认非集群】rules.add(rule);FlowRuleManager.loadRules(rules);
}