整合营销理论主要指,漳州网站优化,中职示范校建设网站,怎么制作一个网站及小程序滑动窗口限流算法是一种基于时间窗口的流量控制策略#xff0c;它将时间划分为固定大小的窗口#xff0c;并在每个窗口内记录请求次数。通过动态滑动窗口#xff0c;算法能够灵活调整限流速率#xff0c;以应对流量的波动。
算法核心步骤
统计窗口内的请求数量#xff1…滑动窗口限流算法是一种基于时间窗口的流量控制策略它将时间划分为固定大小的窗口并在每个窗口内记录请求次数。通过动态滑动窗口算法能够灵活调整限流速率以应对流量的波动。
算法核心步骤
统计窗口内的请求数量记录当前时间窗口内的请求次数。应用限流规则根据预设的阈值判断是否允许当前请求通过。
Redis有序集合的应用
Redis的有序集合Sorted Set为滑动窗口限流提供了理想的实现方式。每个有序集合的成员都有一个分数score我们可以利用分数来定义时间窗口。每当有请求进入时将当前时间戳作为分数并将请求的唯一标识作为成员添加到集合中。这样通过统计窗口内的成员数量即可实现限流。
实现细节
Redis命令简化
通过Redis的ZADD命令我们可以将请求的时间戳和唯一标识添加到有序集合中
ZADD 资源标识 时间戳 请求标识Java代码实现
以下是基于Java和Redis的滑动窗口限流实现
public boolean isAllow(String key) {ZSetOperationsString, String zSetOperations stringRedisTemplate.opsForZSet();long currentTime System.currentTimeMillis();long windowStart currentTime - period;zSetOperations.removeRangeByScore(key, 0, windowStart);Long count zSetOperations.zCard(key);if (count threshold) {return false;}String value 请求唯一标识如请求流水号、哈希值、MD5值等;zSetOperations.add(key, value, currentTime);stringRedisTemplate.expire(key, period, TimeUnit.MILLISECONDS);return true;
}Lua脚本优化
为了确保在高并发场景下的原子性操作我们可以将上述逻辑封装为Lua脚本
local key KEYS[1]
local current_time tonumber(ARGV[1])
local window_size tonumber(ARGV[2])
local threshold tonumber(ARGV[3])
redis.call(ZREMRANGEBYSCORE, key, 0, current_time - window_size)
local count redis.call(ZCARD, key)
if count threshold thenreturn tostring(0)
elseredis.call(ZADD, key, tostring(current_time), current_time)return tostring(1)
end完整Java代码
package com.example.demo.controller;import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.stereotype.Service;import java.util.Collections;
import java.util.concurrent.TimeUnit;Service
public class SlidingWindowRatelimiter {private long period 60 * 1000; // 1分钟private int threshold 3; // 3次Autowiredprivate StringRedisTemplate stringRedisTemplate;public boolean isAllow(String key) {ZSetOperationsString, String zSetOperations stringRedisTemplate.opsForZSet();long currentTime System.currentTimeMillis();long windowStart currentTime - period;zSetOperations.removeRangeByScore(key, 0, windowStart);Long count zSetOperations.zCard(key);if (count threshold) {return false;}String value 请求唯一标识如请求流水号、哈希值、MD5值等;zSetOperations.add(key, value, currentTime);stringRedisTemplate.expire(key, period, TimeUnit.MILLISECONDS);return true;}public boolean isAllow2(String key) {String luaScript local key KEYS[1]\n local current_time tonumber(ARGV[1])\n local window_size tonumber(ARGV[2])\n local threshold tonumber(ARGV[3])\n redis.call(ZREMRANGEBYSCORE, key, 0, current_time - window_size)\n local count redis.call(ZCARD, key)\n if count threshold then\n return tostring(0)\n else\n redis.call(ZADD, key, tostring(current_time), current_time)\n return tostring(1)\n end;long currentTime System.currentTimeMillis();DefaultRedisScriptString redisScript new DefaultRedisScript(luaScript, String.class);String result stringRedisTemplate.execute(redisScript, Collections.singletonList(key), String.valueOf(currentTime), String.valueOf(period), String.valueOf(threshold));return 1.equals(result);}
}AOP实现限流
为了更方便地应用限流策略我们可以通过AOP面向切面编程来拦截请求并应用限流规则。
自定义注解
首先定义一个限流注解
package com.example.demo.controller;import java.lang.annotation.*;Documented
Target(ElementType.METHOD)
Retention(RetentionPolicy.RUNTIME)
public interface RateLimit {long period() default 60; // 窗口大小默认60秒long threshold() default 3; // 阈值默认3次
}切面实现
然后实现一个切面来拦截带有RateLimit注解的方法
package com.example.demo.controller;import jakarta.servlet.http.HttpServletRequest;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ZSetOperations;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;Slf4j
Aspect
Component
public class RateLimitAspect {Autowiredprivate StringRedisTemplate stringRedisTemplate;Before(annotation(rateLimit))public void doBefore(JoinPoint joinPoint, RateLimit rateLimit) {long period rateLimit.period();long threshold rateLimit.threshold();HttpServletRequest httpServletRequest ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();String uri httpServletRequest.getRequestURI();Long userId 123L; // 模拟获取用户IDString key limit: userId : uri;ZSetOperationsString, String zSetOperations stringRedisTemplate.opsForZSet();long currentTime System.currentTimeMillis();long windowStart currentTime - period * 1000;zSetOperations.removeRangeByScore(key, 0, windowStart);Long count zSetOperations.zCard(key);if (count threshold) {throw new RuntimeException(请求过于频繁);} else {zSetOperations.add(key, String.valueOf(currentTime), currentTime);stringRedisTemplate.expire(key, period, TimeUnit.SECONDS);}}
}使用注解
最后在需要限流的方法上添加RateLimit注解
RestController
RequestMapping(/hello)
public class HelloController {RateLimit(period 30, threshold 2)GetMapping(/sayHi)public void sayHi() {}
}总结
通过Redis有序集合和Lua脚本我们实现了一个高效且灵活的滑动窗口限流算法。结合AOP我们可以轻松地将限流策略应用到具体的业务方法中。对于更复杂的流量控制需求可以参考阿里巴巴的Sentinel框架。
参考链接
Sentinel官方文档AOP实现限流Redis Lua脚本