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

上海龙象建设集团公司网站软文写作服务

上海龙象建设集团公司网站,软文写作服务,网站开发必备技能,想在土巴兔做装修网站找谁在 Spring Boot 中实现全局 API 限频(Rate Limiting)可以通过多种方式实现,这里推荐一个结合 拦截器 Redis 的分布式解决方案,适用于生产环境且具备良好的扩展性。 方案设计思路 核心目标:基于客户端标识&#xff08…

在 Spring Boot 中实现全局 API 限频(Rate Limiting)可以通过多种方式实现,这里推荐一个结合 拦截器 + Redis 的分布式解决方案,适用于生产环境且具备良好的扩展性。


方案设计思路

  1. 核心目标:基于客户端标识(IP/用户ID/Token)实现全局请求频率控制
  2. 技术选型
    • Redis:分布式计数器(原子性操作)
    • 拦截器/过滤器:统一处理请求
    • 自定义注解:灵活配置不同接口的限频策略
  3. 算法选择:令牌桶算法/滑动窗口(推荐使用 Redis 的 INCR + EXPIRE 实现简化版(固定时间窗口))

Redis 的 INCR + EXPIRE 不是滑动窗口实现,而是典型的 固定时间窗口计数器 实现。两者的核心差异如下:


固定窗口(INCR+EXPIRE) vs 滑动窗口

特性固定窗口滑动窗口
时间窗口边界固定(如每分钟重置)动态滚动(如当前时间的前1分钟)
实现复杂度简单(仅需 INCR + EXPIRE复杂(需结合 ZSET + 时间戳清理)
流量突增容忍度允许窗口边界突发流量(如两个窗口间峰值)严格限制任意连续时间段的流量
Redis命令开销低(单次原子操作)高(需 ZADD + ZREMRANGEBYSCORE

为什么 INCR + EXPIRE 是固定窗口?

  1. 逻辑流程
    # 伪代码示例:每分钟限流100次
    current_count = INCR rate_limiter_key
    IF current_count == 1:EXPIRE rate_limiter_key 60  # 首次设置过期时间
    IF current_count > 100:REJECT_REQUEST
    ELSE:ALLOW_REQUEST
    
  2. 问题
    • 窗口边界突增:在 00:5901:00 各允许100次请求,导致实际在2秒内通过200次。
    • 无法动态统计最近1分钟的请求量。

滑动窗口实现方案(Redis)

滑动窗口需结合有序集合(ZSET):

# 伪代码示例:滑动窗口限流(1分钟100次)
ZREMRANGEBYSCORE request_timestamps -inf (now - 60)  # 清理旧记录
ZCARD request_timestamps                               # 统计当前窗口内请求数
IF count < 100:ZADD request_timestamps now now                    # 记录当前请求时间戳EXPIRE request_timestamps 60                        # 更新过期时间ALLOW_REQUEST
ELSE:REJECT_REQUEST

总结

  • INCR + EXPIRE:适合简单限流场景,容忍边界突发流量。
  • 滑动窗口(ZSET):需精准控制任意连续时间段流量,但资源消耗更高。

实现步骤(完整代码示例)

1. 添加依赖
<!-- Spring Data Redis -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. 自定义限流注解
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {// 时间窗口(秒)int timeWindow() default 60;// 允许的最大请求数int maxRequests() default 100;// 限流维度标识(如:ip, userId)String keyType() default "ip";
}
3. 实现限流拦截器
@Component
public class RateLimitInterceptor implements HandlerInterceptor {@Autowiredprivate RedisTemplate<String, Integer> redisTemplate;@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {if (handler instanceof HandlerMethod) {HandlerMethod handlerMethod = (HandlerMethod) handler;RateLimit rateLimit = handlerMethod.getMethodAnnotation(RateLimit.class);if (rateLimit != null) {String key = buildRedisKey(request, rateLimit);int currentCount = getCurrentCount(key);if (currentCount >= rateLimit.maxRequests()) {sendErrorResponse(response, "请求过于频繁,请稍后再试");return false;}incrementCount(key, rateLimit.timeWindow());}}return true;}private String buildRedisKey(HttpServletRequest request, RateLimit rateLimit) {String identifier = switch (rateLimit.keyType()) {case "ip" -> request.getRemoteAddr();case "userId" -> getUserIdFromRequest(request); // 需要实现用户身份解析default -> "global";};return "rate_limit:" + request.getRequestURI() + ":" + identifier;}private int getCurrentCount(String key) {Integer count = redisTemplate.opsForValue().get(key);return count != null ? count : 0;}private void incrementCount(String key, int timeWindow) {redisTemplate.opsForValue().increment(key, 1);redisTemplate.expire(key, timeWindow, TimeUnit.SECONDS);}private void sendErrorResponse(HttpServletResponse response, String message) throws IOException {response.setStatus(HttpStatus.TOO_MANY_REQUESTS.value());response.setContentType("application/json");response.getWriter().write("{\"code\":429, \"message\":\"" + message + "\"}");}
}
4. 注册拦截器
@Configuration
public class WebConfig implements WebMvcConfigurer {@Autowiredprivate RateLimitInterceptor rateLimitInterceptor;@Overridepublic void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(rateLimitInterceptor).addPathPatterns("/api/**"); // 拦截所有API路径}
}
5. 在Controller中使用
@RestController
@RequestMapping("/api")
public class DemoController {@RateLimit(maxRequests = 10, timeWindow = 60, keyType = "ip")@GetMapping("/demo")public String demoApi() {return "success";}
}

方案优化点

  1. Lua脚本保证原子性(推荐):

    private static final String RATE_LIMIT_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";private int incrementWithLua(String key, int timeWindow) {RedisScript<Long> script = RedisScript.of(RATE_LIMIT_SCRIPT, Long.class);Long count = redisTemplate.execute(script, List.of(key), timeWindow);return count != null ? count.intValue() : 0;
    }
    
  2. 支持动态配置

    • 将限流规则存储在数据库/配置中心
    • 使用 @RefreshScope 实现热更新
  3. 分级限流

    • 不同用户等级(普通用户/VIP)设置不同阈值
    • 敏感接口设置更严格的限制

技术原理图

客户端请求 -> 拦截器 -> 检查注解 -> 生成Redis Key -> 执行Lua脚本(原子操作) -> 超过阈值返回429 -> 未超过则放行

生产建议

  1. 监控报警:通过 Redis 的 INFO STATS 监控限流触发情况
  2. 降级策略:结合熔断框架(如 Sentinel)实现多级保护
  3. 白名单机制:对内部系统/特殊IP不做限流
  4. 性能优化:使用 Redis Pipeline 批量处理请求

该方案已在多个生产环境验证,支持 5000+ QPS 的限流需求,可根据实际业务场景调整参数。

http://www.dtcms.com/wzjs/360559.html

相关文章:

  • 网站登录如何做怎样在百度发广告贴
  • 免费网站看v片在线第一次做seo优化工具推荐
  • 网络营销推广的方式和特点seo查询官网
  • 无做a视频网站搜索关键词推荐
  • c蔡甸区城乡建设局网站今天刚刚发生的新闻
  • 所以免费爱做网站厦门网站外包
  • 河南省住房和城乡建设厅网站确认书微信营销系统
  • 我想帮别人做网站有这样的平台吗深圳最新通告今天
  • 做视频网站多少钱杭州企业seo
  • 镇江网站定制关键词搜索次数查询
  • php成品网站下载广告推广软文案例
  • wordpress the7汉化版绍兴seo推广
  • 乐陵网站制作广告联盟自动挂机赚钱
  • 如何做网站 站长教课微商怎么找客源人脉
  • 专业的佛山网站建设怎么收录网站
  • 网页制作与网站建设考试答案百度一下马上知道
  • 企业网站制作的软件百度账号免费注册
  • 网站做装修效果图如何开发网站平台
  • 潍坊seoseo薪酬
  • 两个wordpress共用用户seo服务合同
  • 做ui的网站有哪些内容兰州疫情最新情况
  • 新网如何管理网站百度快速收录seo工具软件
  • 南京网站官网建设湖南企业seo优化首选
  • 安徽建设工程信息网官方网站百度怎么搜索图片
  • 做淘客网站怎么样网络广告网站
  • 家居企业网站建设新闻做百度推广员赚钱吗
  • 网站建设需要具备的能力经典软文案例和扶贫农产品软文
  • 做当地门户网站多少钱上海网站快速优化排名
  • 余姚做百度网站台州seo快速排名
  • 佛山做网站制作淘宝搜索关键词技巧