SpringBoot Aop 实现简易版的限流器

Java社区交流群

添加小编微信: 37278753,备注进行

话不多说,直接上代码
如果您对Aop不是很了解,请先点击这里Aop 原理

定义一个注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface SysLog {

    String value() default "";

    long limit() default 10;
}

Aop实现限流

@Slf4j
@Aspect
@Configuration
@Order(-1)
public class SysLogAspect {

    private Map<String,AtomicLong> limiter = new ConcurrentHashMap<>(256);

    private final HttpServletRequest request;

    public SysLogAspect(HttpServletRequest request) {
        this.request = request;
    }

    @Around("@annotation(sysLog)")
    public Object getLog(ProceedingJoinPoint joinPoint, SysLog sysLog) throws Throwable {
        Object proceed = null;
        long time = System.currentTimeMillis();
        try {
            proceed = joinPoint.proceed();
            time = System.currentTimeMillis() - time;
            return proceed;
        }
        catch (Throwable throwable) {
            throw throwable;
        }
        finally {
            // 方法执行后
            postOperation(joinPoint,time);

        }
    }

    /**
     * <p>
     *       方法执行完之后的操作
     * </p>
     * @version 1.0.0
     * @author hai ji
     * @since 2020/8/11
     * @param joinPoint
     * @param time
     * @return void
     */
    private void postOperation(ProceedingJoinPoint joinPoint, long time) {
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        //类名
        String className = methodSignature.getDeclaringTypeName() + "." + methodSignature.getName();
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append(className).append(getIpAddr());
        //将类名加 ip 作为 key
        String limitKey = stringBuilder.toString();
        SysLog annotation = methodSignature.getMethod().getAnnotation(SysLog.class);
        String value = annotation.value();
        long limit = annotation.limit();
        inputCheckLimit(limit);
        //首先去缓存根据key 取value
        if(limiter.get(limitKey) != null){
            AtomicLong atomicLong1 = limiter.get(limitKey);
            long li = atomicLong1.get();
            if(li > 0){
                log.info("当前访问访问次数 : {}", atomicLong1);
                atomicLong1.decrementAndGet();
                limiter.put(limitKey, atomicLong1);
                log.info("剩余访问访问次数 : {}", atomicLong1);
            }else {
                limiter(li,limitKey);
            }
        }else {
            AtomicLong atomicLong = new AtomicLong(limit);
            log.info("当前访问访问次数 : {}",atomicLong.get());
            //减少范文次数
            atomicLong.decrementAndGet();
            limiter.put(limitKey,atomicLong);
            log.info("剩余访问访问次数 : {}",atomicLong.get());
        }
        log.info(value+"执行耗时: {} s",time);
    }

    protected void inputCheckLimit(long limit) {
        if(limit < 0){
            throw new RuntimeException("Current limiting times must be greater than 0");
        }
    }

    protected void limiter(Long lo,String limitKey){
        if(lo <= 0){
            //根据key删除
            limiter.remove(limitKey);
            log.error(getIpAddr() +"  The visit is too frequent. Please try again later");
            throw new RuntimeException("The visit is too frequent. Please try again later");
        }
    }

    private String getIpAddr() {
        String ip = request.getHeader("x-forwarded-for");
        if(ip ==null || ip.length() ==0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if(ip ==null || ip.length() ==0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if(ip ==null || ip.length() ==0 || "unknown".equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if("0:0:0:0:0:0:0:1".equalsIgnoreCase(ip)){
            ip = "127.0.0.1";
        }
        return ip;
    }
}

预期效果:

@RestController
@RequestMapping(value = "/test/")
public class TestWeb {

    @SysLog(value = "测试Aop注解",limit = 3)
    @RequestMapping(value = "syslog")
    public String test(){
        return "test";
    }
}

根据IP 进行限流,次数为3;
在这里插入图片描述

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
Spring Boot 中的 AOP (Aspect Oriented Programming) 可以用来实现接口级别的限流功能,通常我们会结合第三方库如 Spring Cloud Gateway、Webrx 或者自定义拦截来完成这个任务。以下是一个简单的概述: 1. 引入依赖:首先,在Spring Boot项目中添加限流库的依赖,比如 Spring Cloud Gateway 提供了 WebFlux 基于令牌桶(Token Bucket)的限流支持。 ```xml <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-gateway</artifactId> </dependency> ``` 2. 定义限流规则:在配置类中设置限流策略,例如限制某个接口每秒的请求次数。Spring Cloud Gateway 使用 `RateLimiter` 来控制。 ```java @Bean public RateLimiter myLimit(RateLimiterConfig config) { // 设置限流参数,如每秒50次请求 return RateLimiter.of("my-limit", config.limitForPeriod(1, TimeUnit.SECONDS), false); } ``` 3. AOP 配置:创建一个切面(Aspect),利用 `@Around` 注解和 `RateLimiter` 对目标方法进行拦截,并在调用之前检查是否达到限流阈值。 ```java @Aspect @Component public class ApiRateLimitingAspect { @Autowired private RateLimiter myLimit; @Around("@annotation(api)") public Object limitApi(ProceedingJoinPoint joinPoint, Api api) throws Throwable { if (!myLimit.tryAcquire()) { throw new RateLimiterRejectedException("Exceeded rate limit"); } // 执行原方法 return joinPoint.proceed(); } // 如果你需要为每个接口定义不同的限流规则,可以使用注解来标记 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface Api { String value() default ""; } } ``` 在这个例子中,我们假设有一个 `Api` 注解用于标记接口,然后在 `limitApi` 方法中对被该注解修饰的方法进行限流

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小杨同学~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值