[redisson]使用redisson限流器Ratelimiter结合自定义注解及AOP实现限流

使用redisson限流器Ratelimiter实现限流,保证服务正常可用

使用的jar包:
implementation 'org.redisson:redisson-spring-boot-starter:3.13.6'
定义自定义注解
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.springframework.web.bind.annotation.Mapping;

import java.lang.annotation.*;

/**
 * redisson限流器
 * @author liurui
 */
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestRateLimiter {

    /**
     * 限流的key
     * @return
     */
    String key();

    /**
     * 限流模式,默认单机
     * @return
     */
    RateType type() default RateType.PER_CLIENT;

    /**
     * 限流速率,默认每秒10
     * @return
     */
    long rate() default 10;

    /**
     * 限流速率
     * @return
     */
    long rateInterval() default 1000;

    /**
     * 限流速率单位
     * @return
     */
    RateIntervalUnit timeUnit() default RateIntervalUnit.MILLISECONDS;

}

定义AOP切面
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.redisson.api.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;
import java.util.Objects;

@Aspect
@Component
public class RequestRateLimitAspect {

    @Autowired
    private RedissonClient redisson;

    /**
     * 根据自定义注解获取切点
     * @param requestRateLimiter
     */
    @Pointcut("@annotation(requestRateLimiter)")
    public void accessLimit(RequestRateLimiter requestRateLimiter) {
    }

    @Around(value = "accessLimit(requestRateLimiter)", argNames = "pjp,requestRateLimiter")
    public Object around(ProceedingJoinPoint pjp, RequestRateLimiter requestRateLimiter) throws Throwable {
        // 限流拦截器
        RRateLimiter rRateLimiter = getRateLimiter(requestRateLimiter);
        if (rRateLimiter.tryAcquire(1)) {
            return pjp.proceed();
        } else {
            return buildErrorResult();
        }
    }

    /**
     * 获取限流拦截器
     * @param requestRateLimiter
     * @return
     */
    private RRateLimiter getRateLimiter(RequestRateLimiter requestRateLimiter){
        RRateLimiter rRateLimiter = redisson.getRateLimiter(StringUtils.isBlank(requestRateLimiter.key()) ? "default:limiter" : requestRateLimiter.key());
        // 设置限流
        if(rRateLimiter.isExists()) {
            RateLimiterConfig rateLimiterConfig = rRateLimiter.getConfig();
            // 判断配置是否更新,如果更新,重新加载限流器配置
            if (!Objects.equals(requestRateLimiter.rate(), rateLimiterConfig.getRate())
                    || !Objects.equals(requestRateLimiter.timeUnit().toMillis(requestRateLimiter.rateInterval()), rateLimiterConfig.getRateInterval())
                    || !Objects.equals(requestRateLimiter.type(), rateLimiterConfig.getRateType())) {
                rRateLimiter.delete();
                rRateLimiter.trySetRate(requestRateLimiter.type(), requestRateLimiter.rate(), requestRateLimiter.rateInterval(), requestRateLimiter.timeUnit());
            }
        } else {
            rRateLimiter.trySetRate(requestRateLimiter.type(), requestRateLimiter.rate(), requestRateLimiter.rateInterval(), requestRateLimiter.timeUnit());
        }

        return rRateLimiter;
    }

    /**
     * 没有限流令牌返回结果
     * @return
     */
    public Object buildErrorResult() {
        Map<String, Object> map = new HashMap<String, Object>();
        map.put("code", "-1");
        map.put("message", "Too many people visit");
        return map;
    }

}

定义url请求:
import org.redisson.api.RateIntervalUnit;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author liurui
 * @date 2021/11/17
 */
@RestController
@RequestMapping("/redis/test")
public class RedisController {

    private final Logger logger = LoggerFactory.getLogger(RedisController.class);

    @Autowired
    private RedisService redisService;

    @RequestMapping("/add")
    // 为方便测试,每两秒生成一个限流令牌
    @RequestRateLimiter(key = "test", rate = 1, rateInterval = 2, timeUnit = RateIntervalUnit.SECONDS)
    public Object add(String data) {
        logger.info("进入请求");
        redisService.add("testkey", data);
        return "成功";
    }
}

jmeter压测配置:
在这里插入图片描述
在这里插入图片描述

jmeter压测结果:

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在声明的上述自定义注解和AOP切面之后,只需要在需要使用限流的请求方法上声明对应的自定义注解及参数配置即可实现限流。

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
可以使用Redisson提供的分布式限流工具来实现定义注解接口限流。具体实现步骤如下: 1. 定义一个注解,例如@RateLimiter,用于标记需要进行限流的接口方法。 2. 在注解定义相关属性,例如限流的速率、限流的时间单位等。 3. 使用AOP技术,在接口方法执行前判断当前请求是否超过了限流速率,如果超过则拒绝请求。 4. 在AOP切面中使用Redisson提供的分布式锁来实现限流。 下面是一个简单的示例代码: ```java @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface RateLimiter { int rate() default 10; // 速率 TimeUnit timeUnit() default TimeUnit.SECONDS; // 时间单位 } @Aspect @Component public class RateLimiterAspect { private final RedissonClient redissonClient; public RateLimiterAspect(RedissonClient redissonClient) { this.redissonClient = redissonClient; } @Around("@annotation(rateLimiter)") public Object rateLimit(ProceedingJoinPoint joinPoint, RateLimiter rateLimiter) throws Throwable { String key = "rate_limiter:" + joinPoint.getSignature().toLongString(); RRateLimiter limiter = redissonClient.getRateLimiter(key); limiter.trySetRate(RateType.OVERALL, rateLimiter.rate(), rateLimiter.timeUnit()); if (limiter.tryAcquire()) { return joinPoint.proceed(); } else { throw new RuntimeException("接口访问过于频繁,请稍后再试!"); } } } ``` 在上面的代码中,我们定义了一个@RateLimiter注解,并在AOP切面中使用Redisson提供的RRateLimiter实现限流。在接口方法执行前,我们会先获取到当前接口方法的签名,然后使用签名作为Redisson的key,创建一个RRateLimiter对象,并设置限流速率。最后,我们使用tryAcquire()方法来尝试获取锁,如果获取成功则执行接口方法,否则抛出异常。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值