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

JAVA限流方法

在 Java 项目中限制短时间内的频繁访问(即接口限流),是保护系统资源、防止恶意攻击或高频请求导致过载的重要手段。常见实现方案可分为单机限流分布式限流,以下是具体实现方式:

一、核心限流算法

无论哪种方案,底层通常基于以下算法:

  1. 固定窗口计数器:将时间划分为固定窗口(如 1 秒),统计窗口内请求数,超过阈值则拒绝。
    • 优点:简单易实现;缺点:窗口交界处可能出现 “突增流量”(如窗口边缘允许双倍阈值请求)。
  2. 滑动窗口:将固定窗口拆分为多个小窗口,实时滑动计算请求数,解决临界问题。
  3. 令牌桶:匀速生成令牌放入桶中,请求需获取令牌才能处理,支持突发流量(桶内令牌可累积)。
  4. 漏桶:请求先进入桶中,系统以固定速率处理,平滑流量波动(不支持突发流量)。

二、单机限流实现(适用于单实例服务)

1. 基于 Guava 的 RateLimiter(令牌桶算法)

Guava 提供了现成的RateLimiter工具类,适合快速实现单机限流。

依赖引入

<dependency><groupId>com.google.guava</groupId><artifactId>guava</artifactId><version>31.1-jre</version>
</dependency>

代码示例(结合 Spring 拦截器)

// 1. 定义限流拦截器
public class RateLimitInterceptor implements HandlerInterceptor {// 每秒允许10个请求(令牌桶算法)private final RateLimiter rateLimiter = RateLimiter.create(10.0);@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 尝试获取令牌,无令牌则拒绝if (!rateLimiter.tryAcquire()) {response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());response.getWriter().write("请求过于频繁,请稍后再试");return false;}return true;}
}// 2. 注册拦截器(Spring配置)
@Configuration
public class WebConfig implements WebMvcConfigurer {@Overridepublic void addInterceptors(InterceptorRegistry registry) {// 对指定路径生效(如所有接口)registry.addInterceptor(new RateLimitInterceptor()).addPathPatterns("/**");}
}
2. 基于滑动窗口的自定义实现

适合需要更精细控制的场景(如按 IP 限流):

public class SlidingWindowLimiter {// 窗口大小(毫秒)private final long windowSize;// 窗口内最大请求数private final int maxRequests;// 记录每个时间片的请求数(key:时间片起始时间,value:请求数)private final ConcurrentHashMap<Long, Integer> timeSliceCounts = new ConcurrentHashMap<>();public SlidingWindowLimiter(long windowSize, int maxRequests) {this.windowSize = windowSize;this.maxRequests = maxRequests;}public synchronized boolean tryAcquire() {long now = System.currentTimeMillis();// 计算当前窗口的起始时间long windowStart = now - windowSize;// 移除过期的时间片timeSliceCounts.keySet().removeIf(timestamp -> timestamp < windowStart);// 统计当前窗口总请求数int totalRequests = timeSliceCounts.values().stream().mapToInt(Integer::intValue).sum();if (totalRequests < maxRequests) {// 记录当前时间片的请求(精确到100ms,可调整精度)long currentSlice = now - (now % 100);timeSliceCounts.put(currentSlice, timeSliceCounts.getOrDefault(currentSlice, 0) + 1);return true;}return false;}
}// 使用示例(在Controller中)
@RestController
public class TestController {// 10秒内最多允许5次请求(按IP限流)private final Map<String, SlidingWindowLimiter> ipLimiters = new ConcurrentHashMap<>();@GetMapping("/test")public String test(HttpServletRequest request) {String ip = request.getRemoteAddr();// 为每个IP创建独立的限流器SlidingWindowLimiter limiter = ipLimiters.computeIfAbsent(ip, k -> new SlidingWindowLimiter(10000, 5));if (!limiter.tryAcquire()) {return "IP:" + ip + " 请求过于频繁";}return "请求成功";}
}

三、分布式限流(适用于多实例集群)

单机限流无法跨服务实例共享状态,分布式场景需借助中间件(如 Redis)实现全局计数。

基于 Redis + Lua 脚本(滑动窗口算法)

利用 Redis 的原子性和 Lua 脚本保证限流逻辑的一致性:

Lua 脚本(限流逻辑)

-- 限流key(如:接口名:IP)
local key = KEYS[1]
-- 窗口大小(毫秒)
local windowSize = tonumber(ARGV[1])
-- 最大请求数
local maxRequests = tonumber(ARGV[2])
-- 当前时间
local now = tonumber(ARGV[3])-- 窗口起始时间
local windowStart = now - windowSize-- 移除窗口外的请求记录
redis.call('ZREMRANGEBYSCORE', key, 0, windowStart)
-- 统计当前窗口内的请求数
local currentCount = redis.call('ZCARD', key)if currentCount < maxRequests then-- 记录当前请求时间戳redis.call('ZADD', key, now, now .. ':' .. math.random())-- 设置key过期时间(避免内存泄漏)redis.call('EXPIRE', key, windowSize / 1000 + 1)return 1  -- 允许请求
endreturn 0  -- 拒绝请求

Java 代码调用

@Component
public class RedisRateLimiter {@Autowiredprivate StringRedisTemplate redisTemplate;// 加载Lua脚本private final DefaultRedisScript<Long> limitScript;public RedisRateLimiter() {limitScript = new DefaultRedisScript<>();limitScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("limit.lua")));limitScript.setResultType(Long.class);}/*** 尝试获取请求权限* @param key 限流标识(如:"api:test:192.168.1.1")* @param windowSize 窗口大小(毫秒)* @param maxRequests 最大请求数* @return 是否允许*/public boolean tryAcquire(String key, long windowSize, int maxRequests) {Long result = redisTemplate.execute(limitScript,Collections.singletonList(key),String.valueOf(windowSize),String.valueOf(maxRequests),String.valueOf(System.currentTimeMillis()));return result != null && result == 1;}
}// 在Controller中使用
@RestController
public class TestController {@Autowiredprivate RedisRateLimiter redisRateLimiter;@GetMapping("/test")public String test(HttpServletRequest request) {String ip = request.getRemoteAddr();String key = "api:test:" + ip;// 10秒内最多5次请求boolean allowed = redisRateLimiter.tryAcquire(key, 10000, 5);if (!allowed) {return "请求过于频繁,请稍后再试";}return "请求成功";}
}

四、成熟框架推荐

生产环境中,推荐使用现成的限流框架简化开发:

  1. Sentinel:阿里开源的流量控制框架,支持限流、熔断、降级,可通过注解或配置中心动态调整规则。
  2. Resilience4j:轻量级熔断限流框架,支持令牌桶、滑动窗口等多种算法,适合 Spring Boot 项目。
  3. Spring Cloud Gateway:网关层限流(如基于 Redis 的RequestRateLimiter过滤器),适合在入口层统一限流。

总结

  • 单机服务:优先使用 Guava 的RateLimiter或自定义滑动窗口(简单场景)。
  • 分布式服务:必须基于 Redis 等中间件实现全局限流,配合 Lua 脚本保证原子性。
  • 复杂场景:直接集成 Sentinel 等成熟框架,减少重复开发并支持动态配置。

根据业务需求(如限流粒度:IP / 用户 / 接口、是否允许突发流量)选择合适的方案即可。

http://www.dtcms.com/a/347967.html

相关文章:

  • System.IO.Pipelines 与“零拷贝”:在 .NET 打造高吞吐二进制 RPC
  • 【SpringBoot集成篇】SpringBoot 深度集成 Elasticsearch 搜索引擎指南
  • rust语言 (1.88) egui (0.32.1) 学习笔记(逐行注释)(十五)网格布局
  • rust语言 (1.88) egui (0.32.1) 学习笔记(逐行注释)(十三)菜单、右键菜单
  • 【JavaEE】了解synchronized
  • 大数据毕业设计选题推荐-基于大数据的丙型肝炎患者数据可视化分析系统-Hadoop-Spark-数据可视化-BigData
  • 【数据结构】从基础到实战:全面解析归并排序与计数排序
  • 基于stm32汽车雨刮器控制系统设计
  • Java基础第3天总结(面向对象)
  • Shell Case 条件语句详解
  • EP01:【DA】数据分析的概述
  • 01Shell脚本入门:基础命令与变量解析
  • JVM之【类加载系统】
  • 【Qt开发】常用控件(六)
  • Golang云端编程深度指南:架构本质与高阶实践
  • Flink Slot 不足导致任务Pending修复方案
  • 互联网大厂Java面试实录:从Spring到微服务的全面考察
  • 【软件安全】ARM64、x86、32 位与 64 位架构的区别、定义、应用背景
  • 个人搭建小网站教程(云服务器Ubuntu版本)
  • 【数据结构】二叉树的顺序存储、堆的实现及其应用:堆排序与Top-K问题
  • 以国产IoTDB为代表的主流时序数据库架构与性能深度选型评测
  • kanass V1.1.4版本发布,支持Mysql数据库、ubuntu安装与Mantis数据导入
  • Thonny+MicroPython搭建ESP32芯片开发环境
  • 代码性能测试——benchmark库
  • Elasticsearch Ruby 客户端故障排查实战指南
  • AI与SEO关键词协同优化
  • DBeaver连接SQL Server集成认证问题解决方案
  • xxl-job 启动后导致pod内存使用率持续增加
  • 从 Unity UGUI 到 Unreal UMG 的交互与高效实践:UI 事件、坐标系适配与性能优化
  • MATLAB 与 Simulink 联合仿真:控制系统建模与动态性能优化