SpringBoot中间件——封装限流器

背景

    通常能知道一个系统服务在正产增速下流量大小,扩容与压测也是基于此。若有突发或者恶意攻击访问,都要将流量拦截在外。这部分功能不属于业务侧,它是通用非业务的共性需求,所以我们将共性抽取为限流中间件。

方案设计

图解:

  • Ratelimiter
        谷歌Guava库中的一个限流工具类,用于限制访问限制某一资源,令牌桶思想的一个实现,可实现流量控制。
        用到其两个方法:
  • create():创建一个RateLimiter实例,并设置该实例的限流速率(即允许每秒执行的任务数),例如:
RateLimiter rateLimiter = RateLimiter.create(10);
        上述代码将创建一个每秒允许10个任务的RateLimiter实例。

  • tryAcquire():尝试获取一个许可,若获取成功则返回true,否则返回false。 例如:
    if (rateLimiter.tryAcquire()) {
        // do something
    }
        上述代码将尝试获取一个许可,如果该实例当前拥有足够的许可,则执行do something操作
        使用自定义注解和AOP,拦截需要被限流保护的方法。
        拦截后,用Ratelimiter做限流处理
  • 使用自定义注解和AOP,拦截需要被限流保护的方法。
  • 拦截后,用Ratelimiter做限流处理

代码示例

1.自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DoRateLimiter {
    // 每秒访问次数限制
    public double rate() default 0d;
    // 超频后的触发熔断默认响应结果
    public String returnJson() default "";
}

2.常量类

public class Constant {
    /**
     * 全局Hashmap,用于存放【方法】对应的限流类
     * key: 类名 + 方法名
     * value: 对应限流类
     */
    public static Map<String, RateLimiter> stringRateLimiterMap = Collections.synchronizedMap(new HashMap<String, RateLimiter>());
}

3.切面逻辑处理

PS:切面中的实现细节/注解的使用已经在统一白名单中间件  文章中详细梳理过了,需要请移步

@Component
@Aspect
public class RateLimiterHandle {

    // 切点
    @Pointcut("@annotation(com.example.ratelimiteer.annotation.DoRateLimiter)")
    public void aopPointcut() {
    }
    
    /**
     * @param jp            切点
     * @param doRateLimiter 注解实例
     * @return 方法响应结果
     * @throws Throwable
     */
    @Around(value = "aopPointcut()&& @annotation(doRateLimiter)")
    public Object handle(ProceedingJoinPoint jp, DoRateLimiter doRateLimiter) throws Throwable {

        // 判断是否开启限流
        if (doRateLimiter.rate() == 0d) {
            return jp.proceed();
        }

        // 构建限流哈希Map的Key: 类名 + "." + "方法名"
        String className = jp.getTarget().getClass().getName();
        MethodSignature signature = (MethodSignature) jp.getSignature();
        Method method = jp.getTarget().getClass().getMethod(signature.getMethod().getName(), signature.getMethod().getParameterTypes());
        String methodName = method.getName();
        String key = className + "." + methodName;

        // 第一次走到该接口
        if (!Constant.stringRateLimiterMap.containsKey(key)) {
            Map<String, RateLimiter> rateLimiterMap = Constant.stringRateLimiterMap;
            rateLimiterMap.put(key, RateLimiter.create(doRateLimiter.rate()));

        }
        // 判断是否限流
        RateLimiter rateLimiter = Constant.stringRateLimiterMap.get(key);
        if (rateLimiter.tryAcquire()) {
            // 未超过访问限制,则继续执行对应业务逻辑
            return jp.proceed();
        } else {
            // 超过访问限制,则返回默认熔断响应结果(即注解中的returnJson)
            return JSON.parseObject(doRateLimiter.returnJson());
        }
    }
}

测试

controller如下,仅允许一个并发

压测两个并发

result:尝试拿一个许可(RateLimiter.tryAcquire())未拿到(即超频),返回注解中的returnJson()

✅ 由此可见,限流保护已生效

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值