spring boot3自定义注解+拦截器+Redis实现高并发接口限流

发布于:2024-03-29 ⋅ 阅读:(21) ⋅ 点赞:(0)

 

⛰️个人主页:     蒾酒

🔥系列专栏:《spring boot实战》

🌊山高路远,行路漫漫,终有归途


目录

写在前面

内容简介

实现思路

实现步骤

1.自定义限流注解

2.编写限流拦截器

3.注册拦截器

4.接口限流测试


写在前面

本文介绍了springboot开发后端服务中,高并发接口限流设计与实现,坚持看完相信对你有帮助。

同时欢迎订阅springboot系列专栏,持续分享spring boot的使用经验。

内容简介

本文介绍了一种使用自定义注解结合拦截器和redis实现接口限流方法。这种方法也是企业常用方法,是一种比较优雅的解决方案。

优点分析

  1. 灵活性和可定制性: 通过自定义注解和拦截器,可以根据具体的业务需求灵活定义限流规则,满足不同接口的限流需求。

  2. 性能优化: 使用Redis等高性能缓存数据库存储限流计数器,能够有效减轻应用程序的压力,提高系统的性能表现。

  3. 实时性和持久性: Redis具有较高的读写性能,可以实时更新限流计数器,并且数据持久化,保证限流规则的持久性。

  4. 分布式支持: 对于分布式系统,使用Redis等分布式缓存数据库可以方便地实现跨节点的限流策略和计数器共享,确保限流的准确性和一致性。

  5. 成熟稳定: 这种方法经过实践验证,在众多企业项目中得到广泛应用,被认为是一种成熟、稳定且可靠的解决方案。

实现思路

通过自定义一个注解标注需要进行限流的接口方法,通过拦截器对标记改注解的方法进行拦截处理

将同一ip访问同一接口的次数缓存到redis,拦截器中进行判断处理,达到访问阈值直接拒绝。

实现步骤

1.自定义限流注解

import java.lang.annotation.*;

/**
 * @author mijiupro
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Inherited
@Documented
public @interface AccessLimit {
    int limit() default 10; // 限流阈值
    int seconds() default 60; // 时间窗口
}

2.编写限流拦截器

/**
 * 接口限流拦截器
 * @author mijiupro
 */
@Slf4j
@Component
public class AccessLimitInterceptor implements HandlerInterceptor {

    private final StringRedisTemplate redisTemplate;

    public AccessLimitInterceptor(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public boolean preHandle(@NotNull HttpServletRequest request, @NotNull HttpServletResponse response, @NotNull Object handler)  {
        if (!(handler instanceof HandlerMethod handlerMethod)) {
            return true;
        }
        Method method = handlerMethod.getMethod();
        AccessLimit accessLimit = method.getAnnotation(AccessLimit.class);
        // 若方法上没有AccessLimit注解,直接放行
        if (accessLimit == null) {
            return true;
        }

        int limit = accessLimit.limit();
        int seconds = accessLimit.seconds();
        String key = generateKey(request); // 生成限流key

        // 使用基本类型long接收计数值,并确保不会因自动装箱产生NullPointerException
        Long countResult = redisTemplate.opsForValue().increment(key, 1);
        long currentCount = countResult != null ? countResult : 0;
        if (currentCount == 1) {
            // 如果是第一次访问,设置过期时间
            redisTemplate.expire(key, seconds, TimeUnit.SECONDS);
            log.debug("设置访问限制计数为1:{}", key);
            return true;
        }

        if (currentCount > limit) {
            log.error("访问超过限制:{}", key);
            throw new RateLimitException(ResultEnum.ACCESS_LIMIT_REACHED);
        }

        log.debug("访问限制计数递增:{}", key);
        return true;
    }

    private String generateKey(HttpServletRequest request) {
        // 组合key的方式可以根据实际业务需要调整,例如考虑方法名称、用户ID等
        return request.getRemoteAddr() + ":" + request.getContextPath() + ":" + request.getServletPath();
    }


}

3.注册拦截器

@Configuration
public class WebConfig implements WebMvcConfigurer {
   
    private final AccessLimitInterceptor accessLimitInterceptor;

    public WebConfig( AccessLimitInterceptor accessLimitInterceptor) {
        this.accessLimitInterceptor = accessLimitInterceptor;
    }
   

    @Override
    public void addInterceptors(@NotNull InterceptorRegistry registry) {
         //添加上接口限流拦截器使之生效
        registry.addInterceptor(accessLimitInterceptor).order(0);
    }
}

4.接口限流测试

随便写个接口标记限流注解进行测试

这里使用swagger3进行测试:

Spring Boot3整合knife4j(swagger3)_springboot3 knife4j-CSDN博客文章浏览阅读2.1k次,点赞39次,收藏52次。Knife4j · 集Swagger2及OpenAPI3为一体的增强解决方案. | Knife4j (xiaominfo.com)作者的使用的spring boot 3.2.2为当前最新版,所以依赖导入最新的knife4j 4.4.0。3.1 增强模式 | Knife4j (xiaominfo.com)好一个spring boot项目且版本为3X,项目可正常启动。快速开始 | Knife4j (xiaominfo.com)接下来配置以下接口文档的作者等信息。@Tag注解:标记接口类别。_springboot3 knife4jhttps://blog.csdn.net/qq_62262918/article/details/135761392?spm=1001.2014.3001.5502

    @GetMapping("/get-int")
    @AccessLimit( limit = 5,  seconds= 60)
    public Integer getInt() {
        return 1;
    }

前五次访问:

第六次访问:

写在最后

spring boot3自定义注解+拦截器+Redis实现高并发接口限流到这里就结束了,本文介绍了一种常见的实现方法。任何问题评论区或私信讨论,欢迎指正。

本文含有隐藏内容,请 开通VIP 后查看