springboot实现自定义注解限流

本文涉及的产品
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
云数据库 Tair(兼容Redis),内存型 2GB
简介: springboot实现自定义注解限流

最近搭建的博客网站,详情被人刷了,特意以此来提醒该加限流处理了

引入依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

自定义注解实现,默认10秒内只能请求5次,当然这个是根据自己的实际情况修改

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RateLimit {
    //5次
    int count() default 5;
    //10秒
    int second() default 10;
}

aop

/**
 * 限流注解
 * Created by PeakGao on 2023/3/2.
 */
@Aspect
@Component
public class IpLimitAspect extends AccessLimitIntercept {
    @Pointcut("@annotation(com.fyg.common.annotation.RateLimit)")
    public void rateLimit() {
    }
    @Before("rateLimit()")
    public void before(JoinPoint point) throws IOException, InterruptedException {
        long beginTime = System.currentTimeMillis();
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        SysLog sysLog = new SysLog();
        RateLimit ipLimit = method.getAnnotation(RateLimit.class);
        if (ipLimit != null) {
            //注解上的描述
            sysLog.setOperation(String.valueOf(ipLimit.count()) + String.valueOf(ipLimit.second()));
        }
        //请求的参数
        Object[] args = point.getArgs();
        try {
            String params = new Gson().toJson(args);
            sysLog.setParams(params);
        } catch (Exception e) {
        }
        //请求的方法名
        String className = point.getTarget().getClass().getName();
        String methodName = signature.getName();
        sysLog.setMethod(className + "." + methodName + "()");
        //获取request
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        HttpServletResponse response = HttpContextUtils.getHttpServletResponse();
//        try {
        this.preHandle(request, response, ipLimit.count(), ipLimit.second());
//        } catch (Exception e) {
//            logger.error("限流内部程序出错:{}", e.getMessage());
//        }
        // 执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        logger.info("API:{},限流注解程序执行:{} 毫秒", request.getRequestURI(), time);
    }

具体实现逻辑,采用ip限制控流

/**
 * 接口限流
 * 同一个接口10s内请求超过5次进行限流
 * Created by PeakGao on 2023/3/2.
 */
public class AccessLimitIntercept extends BaseController {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, int count, int second) throws InterruptedException, IOException {
        //文章搜索则不进行限流,如需部分接口地址限流可自定义注解实现
        // 拼接redis key = IP + Api限流
        String prefix = Constant.REDIS_KEY_PREFIX + Constant.RATE_LIMIT;
        String key =  ipUtils.getIpAddr(request) + request.getRequestURI();
        // 获取redis的value
        Integer maxTimes = null;
        Object value = redisUtil.get(prefix+key);
        if (value != null) {
            maxTimes = (Integer) value;
        }
        if (maxTimes == null) {
            // 如果redis中没有该ip对应的时间则表示第一次调用,保存key到redis
            redisUtil.set(Constant.REDIS_KEY_PREFIX + Constant.RATE_LIMIT, key, 1, second);
        } else if (maxTimes < count) {
            // 如果redis中的时间比注解上的时间小则表示可以允许访问,这是修改redis的value时间
            redisUtil.set(Constant.REDIS_KEY_PREFIX + Constant.RATE_LIMIT, key, maxTimes + 1, second);
        } else {
            // 请求过于频繁
            output(response, "{\"code\":\"8002\",\"message\":\"请求过于频繁,请稍后再试\"}");
            throw new RuntimeException("API请求限流拦截启动,当前接口:【" + key + "】请求过于频繁");
        }
        return true;
    }
    public void output(HttpServletResponse response, String msg) throws IOException {
        response.setContentType("application/json;charset=UTF-8");
        ServletOutputStream outputStream = null;
        try {
            outputStream = response.getOutputStream();
            outputStream.write(msg.getBytes(StandardCharsets.UTF_8));
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (ObjectUtils.isNotEmpty(outputStream)) {
                outputStream.flush();
                outputStream.close();
            }
        }
    }
}
相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
目录
相关文章
|
1月前
|
XML Java 数据格式
SpringBoot入门(8) - 开发中还有哪些常用注解
SpringBoot入门(8) - 开发中还有哪些常用注解
52 0
|
1天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
107 73
|
1天前
|
存储 Java Spring
【Spring】获取Bean对象需要哪些注解
@Conntroller,@Service,@Repository,@Component,@Configuration,关于Bean对象的五个常用注解
|
1天前
|
Java Spring
【Spring配置】idea编码格式导致注解汉字无法保存
问题一:对于同一个项目,我们在使用idea的过程中,使用汉字注解完后,再打开该项目,汉字变成乱码问题二:本来a项目中,汉字注解调试好了,没有乱码了,但是创建出来的新的项目,写的注解又成乱码了。
|
10天前
|
NoSQL Java Redis
Spring Boot 自动配置机制:从原理到自定义
Spring Boot 的自动配置机制通过 `spring.factories` 文件和 `@EnableAutoConfiguration` 注解,根据类路径中的依赖和条件注解自动配置所需的 Bean,大大简化了开发过程。本文深入探讨了自动配置的原理、条件化配置、自定义自动配置以及实际应用案例,帮助开发者更好地理解和利用这一强大特性。
55 14
|
28天前
|
前端开发 Java Spring
Spring MVC核心:深入理解@RequestMapping注解
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的核心,它将HTTP请求映射到控制器的处理方法上。本文将深入探讨`@RequestMapping`注解的各个方面,包括其注解的使用方法、如何与Spring MVC的其他组件协同工作,以及在实际开发中的应用案例。
42 4
|
1月前
|
XML JSON Java
SpringBoot必须掌握的常用注解!
SpringBoot必须掌握的常用注解!
68 4
SpringBoot必须掌握的常用注解!
|
28天前
|
前端开发 Java 开发者
Spring MVC中的请求映射:@RequestMapping注解深度解析
在Spring MVC框架中,`@RequestMapping`注解是实现请求映射的关键,它将HTTP请求映射到相应的处理器方法上。本文将深入探讨`@RequestMapping`注解的工作原理、使用方法以及最佳实践,为开发者提供一份详尽的技术干货。
108 2
|
28天前
|
前端开发 Java Spring
探索Spring MVC:@Controller注解的全面解析
在Spring MVC框架中,`@Controller`注解是构建Web应用程序的基石之一。它不仅简化了控制器的定义,还提供了一种优雅的方式来处理HTTP请求。本文将全面解析`@Controller`注解,包括其定义、用法、以及在Spring MVC中的作用。
47 2
|
1月前
|
消息中间件 Java 数据库
解密Spring Boot:深入理解条件装配与条件注解
Spring Boot中的条件装配与条件注解提供了强大的工具,使得应用程序可以根据不同的条件动态装配Bean,从而实现灵活的配置和管理。通过合理使用这些条件注解,开发者可以根据实际需求动态调整应用的行为,提升代码的可维护性和可扩展性。希望本文能够帮助你深入理解Spring Boot中的条件装配与条件注解,在实际开发中更好地应用这些功能。
37 2