注解调用 计数器限流算法 令牌桶限流算法 漏桶限流算法 通过注解调用限流算法

测试类:

//package com.xxx.fxserver.service.utils.limiters;
//
//import com.xxx.fxserver.vo.LimiterEnum;
//
//import java.text.SimpleDateFormat;
//import java.util.Date;
//import java.util.concurrent.CountDownLatch;
//import java.util.concurrent.ExecutorService;
//import java.util.concurrent.Executors;
//import java.util.concurrent.atomic.AtomicInteger;
//
///**
// * @author jangwenhua
// * @description: 测试计数器限流算法
// * @date 2023-07-14 15:10
// */
//public class BaseLimitTest {
//
//    private MbLimiter limiter;
//    private int maxQPS;//填充速率
//
//    public BaseLimitTest(LimiterEnum type, int qps) {
//        this.limiter = MbLimiterFactory.getCountLimiter(type, qps);
//        this.maxQPS = qps;
//    }
//
//    public void test(boolean showLog) throws Exception {
//
//        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
//
//        final CountDownLatch countDownLatch1 = new CountDownLatch(100);
//        final CountDownLatch countDownLatch2 = new CountDownLatch(100);
//        final CountDownLatch countDownLatch3 = new CountDownLatch(100);
//
//        // 用于统计成功请求个数
//        AtomicInteger count1 = new AtomicInteger(0);
//        AtomicInteger count2 = new AtomicInteger(0);
//        AtomicInteger count3 = new AtomicInteger(0);
//
//        // 固定大小线程池
//        ExecutorService executorService = Executors.newFixedThreadPool(100);
//
//        System.out.println("====== Test 1 100 并发 ======");
//
//        // 获取开始时间
//        long startTime = System.currentTimeMillis();
//
//        // 前 1s 平均 50 次,随后不到 1s 内发起 50 次请求
//        for (int i = 0; i < 100; i++) {
//            executorService.execute(() -> {
//                if (limiter.tryAcquire()) {
//                    if (showLog)
//                        System.out.println("success " + sf.format(new Date()));
//                    count1.incrementAndGet();
//                } else {
//                    if (showLog)
//                        System.out.println("failed " + sf.format(new Date()));
//                }
//                countDownLatch1.countDown();
//            });
//            // 计数器算法最多请求20次,出现突刺现象
//            if (i < 50 || i > 80) {
//                Thread.sleep(20);
//            }
//        }
//
//        countDownLatch1.await();
//        System.out.println("Time Used: " + (System.currentTimeMillis() - startTime) / 1000.0 + " success: " + count1);
//
//        Thread.sleep(1000);
//        System.out.println("====== Test 2 每秒钟请求 10 次  ======");
//
//        startTime = System.currentTimeMillis();
//
//        // 功能性测试,每秒平均 10 次请求
//        for (int i = 0; i < 100; i++) {
//            executorService.execute(() -> {
//                if (limiter.tryAcquire()) {
//                    if (showLog)
//                        System.out.println("success " + sf.format(new Date()));
//                    count2.incrementAndGet();
//                } else {
//                    if (showLog)
//                        System.out.println("failed " + sf.format(new Date()));
//                }
//                countDownLatch2.countDown();
//            });
//            // 每 10 次请求延时 1 秒
            if ((i + 1) % 10 == 0) {
                Thread.sleep(1000);
            }
//            Thread.sleep(100);
//        }
//
//        countDownLatch2.await();
//        System.out.println("Time Used: " + (System.currentTimeMillis() - startTime) / 1000.0 + " success: " + count2);
//
//        Thread.sleep(1000);
//        System.out.println("====== Test 3 每秒钟请求 20 次  ======");
//
//        startTime = System.currentTimeMillis();
//
//        // 前 4.5s 平均每秒 20 次请求,4.5s 后并发请求 10 次
//        for (int i = 0; i < 100; i++) {
//            executorService.execute(() -> {
//                if (limiter.tryAcquire()) {
//                    if (showLog)
//                        System.out.println("success " + sf.format(new Date()));
//                    count3.incrementAndGet();
//                } else {
//                    if (showLog)
//                        System.out.println("failed " + sf.format(new Date()));
//                }
//                countDownLatch3.countDown();
//            });
            // 每 10 次请求延时 1 秒
            if ((i + 1) % 20 == 0) {
                Thread.sleep(1000);
            }
//            // 测试下令牌桶算法瞬时处理一定量的请求
//            if (i < 90) {
//                Thread.sleep(50);
//            }
//        }
//
//        countDownLatch3.await();
//        System.out.println("Time Used: " + (System.currentTimeMillis() - startTime) / 1000.0 + " success: " + count3);
//        executorService.shutdown();
//
//    }
//}

枚举算法类型:

package com.xxx.fxserver.vo;

public enum LimiterEnum {
     COUNT_LIMITER,//计数器限流算法
     LEAKY_BUCKET_LIMITER,//可重入锁 漏桶限流算法
     TOKEN_BUCKET_LIMITER//令牌桶限流算法
}

计数器: 

package com.xxx.fxserver.service.utils.limiters;


/**
 * @author jangwenhua
 * @description: 计数器限流算法
 * @date 2023-07-14 15:10
 */
public class CountLimiter extends MbLimiter {

    private int count;                          // 计数器
    private long lastTime;                      // 时间戳

    public CountLimiter(int qps) {
        super(qps);
        count = 0;
        lastTime = 0;
    }

    @Override
    public synchronized boolean tryAcquire() {
        long now = System.currentTimeMillis();
        if (now - lastTime > 1000) {
            lastTime = now>>3<<3;               // 保证时间戳后三位都是0, 更精确
            count = 1;
            return true;
        } else if (count < qps) {
            count++;
            return true;
        } else {
            return false;
        }
    }
}

 漏桶算法:

package com.xxx.fxserver.service.utils.limiters;

import java.util.concurrent.locks.ReentrantLock;

/**
 * @author jangwenhua
 * @description: 漏桶限流算法
 * @date 2023-07-14 15:10
 */
public class LeakyBucketLimiter extends MbLimiter {

    private final long capacity;                                            // 水桶容量, 一秒流光
    private double remainWater;                                             // 目前水桶剩下的水量
    private long lastTime;                                                  // 时间戳
    private ReentrantLock lock = new ReentrantLock();                       // 可重入锁

    LeakyBucketLimiter(int qps) {
        super(qps);
        capacity = qps;
        remainWater = capacity;
        lastTime = 0;
    }

    @Override
    public boolean tryAcquire() {
        lock.lock();
        try {
            long now = System.currentTimeMillis();
            double outWater = ((now - lastTime)/1000.0) * capacity;         // 计算这段时间匀速流出的水
            lastTime = now;
            remainWater = Math.max(0, remainWater - outWater);
            if (remainWater + 1 <= capacity) {
                remainWater += 1;
                long waitingMs = (long)((remainWater / capacity) * 1000);   // 计算刚加入的水滴完全滴出漏桶需要的时间(毫秒)
                lock.unlock();
                try {
                    Thread.sleep(waitingMs);                                // 为实现匀速处理请求,需要阻塞一段时间后再return
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                return true;
            } else return false;
        } finally {
            if (lock.isLocked()) lock.unlock();
        }
    }


    //    @Override
//    public synchronized boolean tryAcquire() {
//        long now = System.currentTimeMillis();
//        double outWater = ((now - lastTime)/1000.0)*capacity;           // 计算这段时间匀速流出的水
//        lastTime = now;
//        if (outWater > remainWater) {
//            // 请求已全部处理完毕
//            remainWater = 1;
//            return true;
//        } else {
//            // 还有未处理的请求
//            remainWater -= outWater;
//            if (remainWater + 1 <= capacity) {
//                remainWater += 1;
//                return true;
//            } else return false;
//        }
//    }
//}
}

 令牌桶算法:

package com.xxx.fxserver.service.utils.limiters;

/**
 * @author jangwenhua
 * @description: 令牌桶限流算法
 * @date 2023-07-14 15:10
 */
public class TokenBucketLimiter extends MbLimiter {

    private final int capacity;                             // 桶内能装多少令牌
    private double curTokenNum;                             // 现在桶内令牌数量(用double存)
    private long lastTime;                                  // 时间戳

    TokenBucketLimiter(int qps) {
        super(qps);
        capacity = qps;
        curTokenNum = 0;
        lastTime = 0;
    }

    @Override
    public synchronized boolean tryAcquire() {
        long now = System.currentTimeMillis();
        double intoToken = (now - lastTime)/1000.0 * capacity;
        lastTime = now;
        if (intoToken + curTokenNum > capacity) {
            // 令牌已放满
            curTokenNum = capacity - 1;
            return true;
        } else if (intoToken + curTokenNum >= 1) {
            // 还有令牌
            curTokenNum += intoToken - 1;
            return true;
        } else {
            curTokenNum += intoToken;
            return false;
        }
    }
}

限流器抽象父类: 

package com.xxx.fxserver.service.utils.limiters;


/**
 * @author jangwenhua
 * @description: 限流器抽象父类
 * @date 2023-07-14 15:10
 */
public abstract class MbLimiter {

    final int qps;//填充速率

    MbLimiter(int qps) {
        this.qps = qps;
    }

    // 获取继续执行的资格(非阻塞)立刻返回成功或失败
    public abstract boolean tryAcquire();

}

 限流器工厂类:

package com.xxx.fxserver.service.utils.limiters;

import com.xxx.fxserver.vo.LimiterEnum;

/**
 * @author jangwenhua
 * @description: 限流器工厂类
 * @date 2023-07-14 15:10
 */
public class MbLimiterFactory {

    public static MbLimiter getCountLimiter(LimiterEnum limiterEnum, int qps) {
        switch (limiterEnum) {
            case COUNT_LIMITER:
                return new CountLimiter(qps);
            case LEAKY_BUCKET_LIMITER:
                return new LeakyBucketLimiter(qps);
            case TOKEN_BUCKET_LIMITER:
                return new TokenBucketLimiter(qps);
            default:
                return null;
        }
    }

}

 定义注解调用参数:

package com.xxx.fxserver.service.utils.limiters;

import com.xxx.fxserver.vo.LimiterEnum;

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)//注解会在class中存在,运行时可通过反射获取
@Target(ElementType.METHOD)//目标是方法
@Documented//文档生成时,该注解将被包含在javadoc中,可去掉
public @interface MbLimiters {
    /**
     * 类型
     */
    LimiterEnum type() default LimiterEnum.COUNT_LIMITER;
    /**
     * 一秒流数
     */
    int qps() default 10;

}

 注解切面核心代码:

package com.xxx.fxserver.service.utils.limiters;

import com.xxx.fxserver.common.exception.BusinessException;
import com.xxx.fxserver.vo.LimiterEnum;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.text.SimpleDateFormat;
import java.util.Date;

@Aspect
@Component
@Slf4j
public class MbLimitersAspect {

    @Pointcut("@annotation(com.xxx.fxserver.service.utils.limiters.MbLimiters)")
    public void controllerAspect() { }

    @Around("controllerAspect()")
    public Object aroundMethod(ProceedingJoinPoint jp) throws Throwable {
        boolean showLog =true;
        MbLimiter limiter = giveController(jp);
        //执行方法
        SimpleDateFormat sf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
            if (limiter.tryAcquire()) {
                if (showLog)
                    log.info("success " + sf.format(new Date()));
            } else {
                if (showLog)
                    log.info("failed " + sf.format(new Date()));
                throw new BusinessException("请重新尝试!");
            }
        return jp.proceed();
    }

    private MbLimiter giveController(ProceedingJoinPoint joinPoint) throws Exception {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();
        if (method != null) {
            MbLimiters limiters = method.getAnnotation(MbLimiters.class);
            MbLimiter limiter = this.getCountLimiter(limiters.type(),limiters.qps());
            return limiter;
        }
        return null;
    }

    public static MbLimiter getCountLimiter(LimiterEnum limiterEnum, int qps) {
        switch (limiterEnum) {
            case COUNT_LIMITER:
                return new CountLimiter(qps);
            case LEAKY_BUCKET_LIMITER:
                return new LeakyBucketLimiter(qps);
            case TOKEN_BUCKET_LIMITER:
                return new TokenBucketLimiter(qps);
            default:
                return null;
        }
    }

}

 接口调用注解方式:

    @PostMapping(value = "/getQrcode")
    @MbLimiters(type = LimiterEnum.TOKEN_BUCKET_LIMITER,qps = 80)
    public ResultVo getQrcode(@RequestBody Map<String, Object> params) throws IOException {
return null;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值