当前位置: 首页 > news >正文

基于Redis分布式的限流

以下是基于 Redis 实现分布式限流的 Java 解决方案,包含多种限流算法和完整实现代码:

一、限流算法选择与实现

1. 固定窗口算法(Simple Rate Limiter)
public class RedisFixedWindowRateLimiter {private final StringRedisTemplate redisTemplate;private final String script = "local current = redis.call('INCR', KEYS[1])\n" +"if current == 1 then\n" +"  redis.call('EXPIRE', KEYS[1], ARGV[1])\n" +"end\n" +"return current > tonumber(ARGV[2])";public RedisFixedWindowRateLimiter(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;}/*** 固定窗口限流* @param key 限流标识* @param period 时间窗口(秒)* @param limit 最大请求数* @return 是否允许请求*/public boolean isAllowed(String key, int period, int limit) {key = "rate_limit:" + key;return !redisTemplate.execute(new DefaultRedisScript<>(script, Boolean.class),Collections.singletonList(key),String.valueOf(period),String.valueOf(limit));}
}
2. 滑动窗口算法(基于ZSet)
public class RedisSlidingWindowRateLimiter {private final StringRedisTemplate redisTemplate;private final RedisScript<Long> script;public RedisSlidingWindowRateLimiter(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;this.script = new DefaultRedisScript<>("local key = KEYS[1]\n" +"local now = tonumber(ARGV[1])\n" +"local window = tonumber(ARGV[2])\n" +"local limit = tonumber(ARGV[3])\n" +"local start = now - window\n" +// 移除窗口外的记录"redis.call('ZREMRANGEBYSCORE', key, 0, start)\n" +// 统计窗口内请求数"local count = redis.call('ZCARD', key)\n" +// 判断是否超出限制"local allowed = count < limit\n" +// 若允许则添加当前请求时间戳"if allowed then redis.call('ZADD', key, now, now) end\n" +// 设置过期时间,避免冷key永久存在"redis.call('EXPIRE', key, window)\n" +"return allowed and 1 or 0",Long.class);}/*** 滑动窗口限流* @param key 限流标识* @param window 窗口大小(毫秒)* @param limit 窗口内最大请求数* @return 是否允许请求*/public boolean isAllowed(String key, long window, int limit) {key = "sliding_window:" + key;long now = System.currentTimeMillis();return redisTemplate.execute(script, Collections.singletonList(key),String.valueOf(now),String.valueOf(window),String.valueOf(limit)) == 1L;}
}
3. 令牌桶算法(Token Bucket)
public class RedisTokenBucketRateLimiter {private final StringRedisTemplate redisTemplate;private final RedisScript<List<Long>> script;public RedisTokenBucketRateLimiter(StringRedisTemplate redisTemplate) {this.redisTemplate = redisTemplate;this.script = new DefaultRedisScript<>("local tokens_key = KEYS[1]\n" +"local timestamp_key = KEYS[2]\n" +"local rate = tonumber(ARGV[1])\n" +  // 令牌生成速率(个/秒)"local capacity = tonumber(ARGV[2])\n" +  // 桶容量"local now = tonumber(ARGV[3])\n" +  // 当前时间戳(毫秒)"local requested = tonumber(ARGV[4])\n" +  // 请求的令牌数"local last_tokens = tonumber(redis.call('GET', tokens_key) or capacity)\n" +"local last_refreshed = tonumber(redis.call('GET', timestamp_key) or 0)\n" +"local delta = math.max(0, now - last_refreshed)\n" +  // 距离上次刷新的时间差"local filled_tokens = math.min(capacity, last_tokens + (delta * rate / 1000))\n" +  // 计算当前令牌数"local allowed = filled_tokens >= requested\n" +  // 是否允许请求"local new_tokens = filled_tokens\n" +"if allowed then\n" +"  new_tokens = filled_tokens - requested\n" +"end\n" +"redis.call('SET', tokens_key, new_tokens, 'EX', 3600)\n" +  // 设置1小时过期"redis.call('SET', timestamp_key, now, 'EX', 3600)\n" +"return { allowed and 1 or 0, new_tokens }",List.class);}/*** 令牌桶限流* @param key 限流标识* @param rate 令牌生成速率(个/秒)* @param capacity 桶容量* @param requested 请求的令牌数(通常为1)* @return 是否允许请求*/public boolean isAllowed(String key, int rate, int capacity, int requested) {key = "token_bucket:" + key;String tokensKey = key + ":tokens";String timestampKey = key + ":timestamp";long now = System.currentTimeMillis();List<Long> result = redisTemplate.execute(script, Arrays.asList(tokensKey, timestampKey),String.valueOf(rate),String.valueOf(capacity),String.valueOf(now),String.valueOf(requested));return result != null && result.get(0) == 1L;}
}

二、注解式限流实现(基于Spring AOP)

1. 自定义限流注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimit {String key() default "rate_limit";int period() default 60;  // 时间窗口(秒)int limit() default 100;  // 最大请求数RateLimitType type() default RateLimitType.COMMON;  // 限流类型
}public enum RateLimitType {COMMON,  // 普通限流IP,      // 基于IP限流USER     // 基于用户限流
}
2. AOP切面实现
@Aspect
@Component
public class RateLimitAspect {private final RedisFixedWindowRateLimiter rateLimiter;public RateLimitAspect(StringRedisTemplate redisTemplate) {this.rateLimiter = new RedisFixedWindowRateLimiter(redisTemplate);}@Around("@annotation(com.example.ratelimit.RateLimit)")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();RateLimit rateLimit = method.getAnnotation(RateLimit.class);String key = getKey(rateLimit, joinPoint);boolean allowed = rateLimiter.isAllowed(key, rateLimit.period(), rateLimit.limit());if (!allowed) {throw new RuntimeException("请求过于频繁,请稍后再试");}return joinPoint.proceed();}private String getKey(RateLimit rateLimit, ProceedingJoinPoint joinPoint) {switch (rateLimit.type()) {case IP:return getClientIp() + "_" + rateLimit.key();case USER:return getUserId() + "_" + rateLimit.key();default:return methodSignatureToKey(joinPoint);}}// 获取客户端IP(实际实现需从Request中获取)private String getClientIp() {// 实现略return "127.0.0.1";}// 获取用户ID(实际实现需从SecurityContext或Session中获取)private String getUserId() {// 实现略return "user_123";}private String methodSignatureToKey(ProceedingJoinPoint joinPoint) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();return signature.getDeclaringTypeName() + "." + signature.getName();}
}

三、限流熔断与降级策略

1. 熔断降级处理器
@Component
public class RateLimitFallbackHandler {/*** 默认降级方法(可自定义返回值)*/public Object defaultFallback(JoinPoint joinPoint, Throwable ex) {return Result.fail("系统繁忙,请稍后再试", 503);}
}
2. 整合Sentinel实现熔断降级
@Service
public class UserService {@RateLimit(key = "getUser", limit = 200)@SentinelResource(value = "getUser", fallback = "getUserFallback")public User getUser(Long userId) {// 业务逻辑return userDao.getUserById(userId);}public User getUserFallback(Long userId, Throwable ex) {// 限流或熔断后的降级逻辑return new User(-1L, "系统繁忙,请稍后再试");}
}

四、配置与使用示例

1. Redis配置
@Configuration
public class RedisConfig {@Beanpublic StringRedisTemplate stringRedisTemplate(RedisConnectionFactory factory) {StringRedisTemplate template = new StringRedisTemplate();template.setConnectionFactory(factory);template.setKeySerializer(new StringRedisSerializer());template.setValueSerializer(new StringRedisSerializer());return template;}
}
2. 使用注解限流
@RestController
@RequestMapping("/api")
public class ApiController {@GetMapping("/data")@RateLimit(key = "getData", period = 60, limit = 100)public ResponseData getData() {// 业务逻辑return ResponseData.success();}
}

五、高级特性与优化建议

1. 限流指标监控
@Component
public class RateLimitMetrics {private final StringRedisTemplate redisTemplate;private final MeterRegistry meterRegistry;public RateLimitMetrics(StringRedisTemplate redisTemplate, MeterRegistry meterRegistry) {this.redisTemplate = redisTemplate;this.meterRegistry = meterRegistry;}/*** 收集限流指标*/@Scheduled(fixedDelay = 60_000)public void collectMetrics() {Set<String> keys = redisTemplate.keys("rate_limit:*");if (keys != null) {for (String key : keys) {String count = redisTemplate.opsForValue().get(key);if (count != null) {String metricName = key.replace("rate_limit:", "ratelimit.");meterRegistry.gauge(metricName, Long.parseLong(count));}}}}
}
2. 动态限流规则
@Service
public class DynamicRateLimitService {private final StringRedisTemplate redisTemplate;private final Map<String, RateLimitConfig> configMap = new ConcurrentHashMap<>();@PostConstructpublic void init() {// 从配置中心加载规则loadRulesFromConfigCenter();// 注册配置变更监听器registerConfigChangeListener();}public boolean isAllowed(String key) {RateLimitConfig config = configMap.get(key);if (config == null) {return true;}return new RedisFixedWindowRateLimiter(redisTemplate).isAllowed(key, config.getPeriod(), config.getLimit());}
}

六、不同限流算法对比与选择

算法优点缺点适用场景
固定窗口实现简单、性能高临界问题(突刺现象)对精度要求不高的场景
滑动窗口解决临界问题占用内存较大需要更精确限流的场景
令牌桶支持突发流量、平滑限流实现复杂对流量平滑度有要求的场景

七、注意事项与最佳实践

  1. 性能优化

    • 使用 Lua 脚本保证原子性
    • 批量操作减少网络开销
    • 合理设置过期时间避免冷key占用内存
  2. 异常处理

    • Redis连接异常时的降级策略(如允许请求或拒绝所有请求)
    • 熔断机制防止级联故障
  3. 监控与告警

    • 监控限流命中率、QPS、拒绝率等指标
    • 设置阈值告警(如拒绝率超过50%时触发)
  4. 分布式一致性

    • 多节点部署时确保时间同步(NTP服务)
    • 优先选择Redis Cluster或哨兵模式保证高可用

通过以上实现,你可以基于 Redis 构建高性能、高可用的分布式限流系统,有效保护后端服务免受流量冲击。

http://www.lryc.cn/news/575853.html

相关文章:

  • JavaScript性能优化
  • Feign 实战指南:从 REST 替代到性能优化与最佳实践
  • 【数据结构】B树的介绍及其实现C++
  • 探访成都芯谷金融中心文化科技产业园:解锁城市发展新密码
  • JDY-23蓝牙模块与电脑的连接方式
  • 024 企业客户管理系统技术解析:基于 Spring Boot 的全流程管理平台
  • JdbcUtils的三个版本
  • 3.web逆向之开发者工具调试
  • Spring-图书管理系统
  • 《Effective Python》第十章 健壮性——显式链接异常,让错误追踪更清晰的艺术
  • 电梯控制系统技术解析:从基础原理到PLC应用
  • Stable Diffusion入门-ControlNet 深入理解 第二课:ControlNet模型揭秘与使用技巧
  • 【RabbitMQ】基于Spring Boot + RabbitMQ 完成应用通信
  • .小故事.
  • Mybatis-Plus源代码走读后记
  • 青少年编程与数学 01-012 通用应用软件简介 15 人工智能助手
  • Rust交互式编程环境Jupyter Lab搭建
  • YOLOv8快速入门
  • HarmonyOS NEXT仓颉开发语言实现画板案例
  • fish安装node.js环境
  • 【开发杂谈】Auto Caption:使用 Electron 和 Python 开发实时字幕显示软件
  • Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory
  • 车联网网络安全渗透测试:深度解析与实践
  • 商品中心—15.库存分桶扣减的技术文档
  • 一款被我拿来处理图片和视频的免费环保软件
  • Web基础关键_003_CSS(一)
  • 小程序学习笔记:加载效果、上拉加载与节流处理
  • Ubuntu安装Docker部署Python Flask Web应用
  • PHP语法基础篇(六):数组
  • 代码随想录|图论|09沉没孤岛