熔断限流切面工具

背景

随着业务不断拓展优化,业务量的急增,导致应用系统性能瓶颈问题越来越显著;请求频繁超时、内部系统卡顿、CUP过高、内存不足、GC频繁;调用外部系统频繁超时,触发限流等情况;针对该情况提供一套熔断限流工具。

应用场景

系统高并发熔断限流

1.漏桶算法熔断限流可以保证外部系统稳定性

建议用于访问外部系统存在系统瓶颈,有限流等情况

2.令牌桶算法熔断限流可以保证内部系统稳定性

建议用于外部系统访问内部系统,内部系统存在瓶颈,性能问题等情况

3.固定窗口算法熔断限流可以到毫秒级熔断限流

根据实际业务场景合理选择,可限流,可控速,确定并发处理不友好,可与淘汰策略结合,使用乐观锁while(cas)自旋淘汰策略

4.强制并发限流

    缺点没有并发时间差预处理浮动,强制返回失败,不够柔和;可根据业务场景选择

技术实现

  1. 自定义切面技术(aspectJ)
  2. Atomic原子类技术
  3. 漏桶算法
  4. 令牌桶算法
  5. 固定窗口算法
  6. CAS乐观锁
  7. ConcurrentHashMap防并发

核心实现

熔断限流实体对象

/**

 *

 * @ClassName: FuseStrategy.java

 * @Description: 熔断限流实体对象

 * @author: ysf

 * @date: 2021年8月1日 上午10:55:01

 */

public class FuseStrategy {

/*

 * 熔断限流唯一标识

 */

private String value;

/*

 * 熔断限流策略0-令牌桶算法,1-漏桶算法,2-固定窗口算法,3当前并发限制

 */

private int fuseStrategy;

/*

 * 熔断限流桶容量

 */

private long bucketLimit;

/*

 * 秒级速率,漏桶算法每秒消费任务数量

 */

private long bucketCustomeRate;

/*

 * 秒级速率,令牌桶算法每秒生产任务数量

 */

private long bucketPushRate;

/*

 * 毫秒速率,固定x时间滑动执行1条任务

 */

private long fixedWaitTime;

/*

 * 熔断限流开始时间

 */

private AtomicLong startTime;

/*

 * 当前请求任务量

 */

private AtomicLong currcentRequestLimit;

/*

 *当前生成任务量

 */

private AtomicLong currcentPushNum;

/*

 *淘汰策略 0-直接结束返回null,1-直接报错,2-CAS等待资源释放继续执行

 */

private int eliminateStrategy;

    ......

    Get...;Set...;构造函数...

......

}

熔断限流切面规则

/**

 *

 * @ClassName: CurrentLimitFuseStrategy.java

 * @Description: 限流熔断切面

 * @author: ysf

 * @date: 2021年8月1日 下午4:18:11

 *

 */

@Target(ElementType.METHOD)

@Retention(RetentionPolicy.RUNTIME)

@Documented

public @interface CurrentLimitFuseStrategy {

/*

 * 熔断限流唯一标识

 */

String value() default "";

/*

 * 熔断限流策略0-令牌桶算法,1-漏桶算法,2-强制并发限制,3-固定窗口算法

 */

int fuseStrategy() default 0;

/*

 * 熔断限流限制并发数量

 */

long bucketLimit() default 20;

/*

 * 单位时间消费任务量,秒级速率,漏桶算法

 */

long bucketCustomeRate() default 20;

/*

 * 单位时间生产任务量,秒级速率,令牌桶算法

 */

long bucketPushRate() default 20;

/*

 * 固定窗口停顿时间 ,毫秒速率,固定窗口算法

 */

long fixedWaitTime() default 100;

/*

 * 淘汰策略 0-直接结束返回null,1-直接报错,2-CAS等待资源释放继续执行

 */

int eliminateStrategy() default 0;

}

熔断限流切面实现

/**

 *

 * @ClassName: FuseStrategyAspect.java

 * @Description: 熔断限流切面

 * @author: ysf

 * @date: 2021年8月1日 上午10:37:52

 */

@Aspect

@Component

public class FuseStrategyAspect {

private static Logger logger = LoggerFactory.getLogger(FuseStrategyAspect.class);

   @Pointcut("@annotation(fills.tools.fuse.CurrentLimitFuseStrategy)")

    public void fusePointCut() {

        

    }

/**

 *

 * @Function: FuseStrategyAspect.java

 * @Description: 熔断限流切面实现方法

 *

 * @param: obj

 * @throws Throwable

 * @return:Object

 *

 * @author: ysf

 * @date: 2021年8月1日 上午10:38:15

 */

@Around (value = "fusePointCut()")

public Object doAround(ProceedingJoinPoint obj) throws Throwable {

        MethodSignature signature = (MethodSignature) obj.getSignature();

        Method method = signature.getMethod();

        CurrentLimitFuseStrategy fuseStrategyConfig = method.getAnnotation(CurrentLimitFuseStrategy.class);

        FuseStrategy fuseStrategy = CurrentLimitFuseCache.getFuseStrategy(fuseStrategyConfig.value());

        if(fuseStrategy==null){

                fuseStrategy = setFuseStrategy(fuseStrategyConfig);

        }

        if(!checkFuseLimit(fuseStrategy)){

                Object res =obj.proceed();

                FuseArithmetic.doReleaseResource(fuseStrategy);

                return res;

        }else{

                if(fuseStrategy.getEliminateStrategy()==                                 FuseEliminateStrategyEnum.ELIMINATE_RETURN_NULL.getIndex()){

                        logger.debug("并发限流触发,结束返回null");

                        return null;

                }else if(fuseStrategy.getEliminateStrategy()==                                 FuseEliminateStrategyEnum.ELIMINATE_RETURN_ERROR.getIndex()){

                        logger.debug("并发限流触发,抛出【Throwable】");

                        throw new Throwable("系统并发触达上限报错");

                }else{

                        logger.info("并发限流触发,while(cas)轮询等待资源释放");

                        while(checkFuseLimit(fuseStrategy));

                        Object res =obj.proceed();

                        FuseArithmetic.doReleaseResource(fuseStrategy);

                        return res;

                }

        }

}

/**

 *

 * @Function: FuseStrategyAspect.java

 * @Description: 熔断限流检验并拦截

 *

 * @param: fuseStrategy

 * @return:boolean

 *

 * @author: ysf

 * @date: 2021年8月1日 上午10:35:41

 */

private boolean checkFuseLimit(FuseStrategy fuseStrategy){

        boolean flag = false;

        if(fuseStrategy.getFuseStrategy()== FuseStrategyEnum.FUSE_BUCKET_ALGORITHM.getIndex()){

                flag= FuseArithmetic.bucketArithmetic(fuseStrategy);

        }else if(fuseStrategy.getFuseStrategy()==                 FuseStrategyEnum.FUSE_TOKEN_BUCKET_ALGORITHM.getIndex()){

                flag= FuseArithmetic.tokenBucketArithmetic(fuseStrategy);

        }else if(fuseStrategy.getFuseStrategy()==                 FuseStrategyEnum.FUSE_FIXED_ALGORITHM.getIndex()){

                flag= FuseArithmetic.fixedLimitArithmetic(fuseStrategy);

        }else if(fuseStrategy.getFuseStrategy()== FuseStrategyEnum.FUSE_LIMIT_ALGORITHM.getIndex()){

                flag= FuseArithmetic.requestLimitArithmetic(fuseStrategy);

        }

        return flag;

}

/**

 *

 * @Function: FuseStrategyAspect.java

 * @Description: 缓存熔断限流实体对象

 * synchronized 防止并发

 * @param: fuseStrategyConfig

 * @param:

 * @return:FuseStrategy

 *

 * @author: ysf

 * @date: 2021年8月1日 上午10:37:05

 *

 */

private synchronized FuseStrategy setFuseStrategy(CurrentLimitFuseStrategy fuseStrategyConfig){

        FuseStrategy fuseStrategy = null;

        int fuseType = fuseStrategyConfig.fuseStrategy();

        if(fuseType==FuseStrategyEnum.FUSE_BUCKET_ALGORITHM.getIndex()){

                fuseStrategy = new FuseStrategy(fuseStrategyConfig.value(), fuseType,                 fuseStrategyConfig.bucketLimit(), fuseStrategyConfig.bucketCustomeRate(),                 fuseStrategyConfig.eliminateStrategy(), new AtomicLong(0L));

        }else if(fuseType==FuseStrategyEnum.FUSE_TOKEN_BUCKET_ALGORITHM.getIndex()){

                fuseStrategy = new FuseStrategy(fuseStrategyConfig.value(), fuseType,                 fuseStrategyConfig.bucketLimit(), fuseStrategyConfig.bucketPushRate(),                 fuseStrategyConfig.eliminateStrategy());

        }else if(fuseType==FuseStrategyEnum.FUSE_FIXED_ALGORITHM.getIndex()){

                fuseStrategy = new FuseStrategy(fuseStrategyConfig.value(), fuseType,                 fuseStrategyConfig.fixedWaitTime(), fuseStrategyConfig.eliminateStrategy(),                 new AtomicLong(0L));

        }else{

                fuseStrategy = new FuseStrategy(fuseStrategyConfig.value(), fuseType,                 fuseStrategyConfig.bucketLimit(), fuseStrategyConfig.eliminateStrategy());

        }

     CurrentLimitFuseCache.setFuseStrategy(fuseStrategy);

     return CurrentLimitFuseCache.getFuseStrategy(fuseStrategy.getValue());

}

}

熔断限流算法实现

/**

 *

 * @ClassName: FuseArithmetic.java

 * @Description: 熔断限流算法

 * @author: ysf

 * @date: 2021年7月30日 下午8:57:40

 *

 */

public class FuseArithmetic {

private static Logger logger = LoggerFactory.getLogger(FuseArithmetic.class);

/**

 *

 * @Function: FuseArithmetic.java

 * @Description: 漏桶算法秒级限流,不限制放入速率,限制出口速率,溢出抛弃,可以防止并发访问外部系统导致崩溃

 * fasle 没有触发限制,true 触发限制

 * @param: fuseStrategy

 * @return:boolean

 *

 * @author: ysf

 * @date: 2021年7月30日 下午8:17:06

 *

 */

public static boolean bucketArithmetic(FuseStrategy fuseStrategy){

        long currcentTime = System.currentTimeMillis();

        //当前时间与上一次时间差

        long leadTime = currcentTime - fuseStrategy.getStartTime().addAndGet(0L);

        //预计释放容量

        long consumeNum = leadTime>0 ? leadTime * fuseStrategy.getBucketCustomeRate()/ 1000 : 0L;

        long requestLimit = 0L;

        if(leadTime>0){

                fuseStrategy.getStartTime().addAndGet(leadTime);

        }

        if(Math.max(0, (requestLimit=fuseStrategy.getCurrcentRequestLimit().incrementAndGet()) - consumeNum)>fuseStrategy.getBucketLimit()){

                logger.debug(fuseStrategy.getValue()+"_true_bucketArithmetic_requestLimit="+requestLimit+",bucketLimit="+fuseStrategy.getBucketLimit()+",leadTime="+leadTime);

                fuseStrategy.getCurrcentRequestLimit().decrementAndGet();

                return true;

}

                logger.debug(fuseStrategy.getValue()+"_false_bucketArithmetic_requestLimit="+requestLimit+",bucketLimit="+fuseStrategy.getBucketLimit()+",leadTime="+leadTime);

                return false;

}

/**

 *

 * @Function: FuseArithmetic.java

 * @Description: 令牌桶算法秒级限流,限制放入速率,不限制出口速率,溢出抛弃,可以缓冲处理急增并发,可以保护内部系统稳定

 * fasle 没有触发限制,true 触发限制

 * @param: fuseStrategy

 * @return:boolean

 *

 * @author: ysf

 * @date: 2021年7月30日 下午8:59:16

 *

 */

public static boolean tokenBucketArithmetic(FuseStrategy fuseStrategy){

        long currcentTime = System.currentTimeMillis();

        //当前时间与上一次时间差

        long leadTime = currcentTime - fuseStrategy.getStartTime().addAndGet(0L);

        //预计增加容量

        long pushNum = leadTime>0 ? leadTime* fuseStrategy.getBucketPushRate()/ 1000 : 0L;

        long currcentPushNum = 0L;

        if(leadTime>0){

                fuseStrategy.getStartTime().addAndGet(leadTime);

        }

        if((currcentPushNum = Math.min(fuseStrategy.getBucketLimit(),fuseStrategy.getCurrcentPushNum().decrementAndGet()+pushNum))>=0){

                logger.debug(fuseStrategy.getValue()+"_false_tokenBucketArithmetic_requestLimit="+currcentPushNum+",bucketLimit="+fuseStrategy.getBucketLimit()+",leadTime="+leadTime);

                return false;

        }

        logger.debug(fuseStrategy.getValue()+"_true_tokenBucketArithmetic_requestLimit="+currcentPushNum+",bucketLimit="+fuseStrategy.getBucketLimit()+",leadTime="+leadTime);

        fuseStrategy.getCurrcentPushNum().incrementAndGet();

        return true;

}

/**

 *

 * @Function: FuseArithmetic.java

 * @Description: 固定窗口算法,每个单位时间内固定并发任务数,超过就熔断限流,可以毫秒及熔断限流,可以用于内部控制急增并发访问/控制访问外部速度(针对外部限流平台)

 * fasle 没有触发限制,true 触发限制

 * @param: fuseStrategy

 * @return:boolean

 *

 * @author: ysf

 * @date: 2021年8月1日 上午10:23:48

 */

public static boolean fixedLimitArithmetic(FuseStrategy fuseStrategy){

        long currcentTime = System.currentTimeMillis();

        //当前时间与上一次时间差

        long leadTime = currcentTime - fuseStrategy.getStartTime().addAndGet(0L);

        Long currcentRequestLimit =0L;

        if(leadTime>0&&leadTime>=fuseStrategy.getFixedWaitTime()){

                fuseStrategy.getCurrcentRequestLimit().lazySet(0);

                currcentRequestLimit = fuseStrategy.getCurrcentRequestLimit().incrementAndGet();

                fuseStrategy.getStartTime().lazySet(currcentTime);

                logger.debug(fuseStrategy.getValue()+"_false_fixedLimitArithmetic_currcentRequestLimit="+currcentRequestLimit+",bucketLimit="+fuseStrategy.getBucketLimit()+",leadTime="+leadTime);

                return false;

        }

        if(fuseStrategy.getBucketLimit()>= (currcentRequestLimit = fuseStrategy.getCurrcentRequestLimit().incrementAndGet()) ){

                logger.debug(fuseStrategy.getValue()+"_false_fixedLimitArithmetic_currcentRequestLimit="+currcentRequestLimit+",bucketLimit="+fuseStrategy.getBucketLimit()+",leadTime="+leadTime);

                return false;

        }else{

                logger.debug(fuseStrategy.getValue()+"_true_fixedLimitArithmetic_currcentRequestLimit="+currcentRequestLimit+",bucketLimit="+fuseStrategy.getBucketLimit()+",leadTime="+leadTime);

                fuseStrategy.getCurrcentRequestLimit().decrementAndGet();

                return true;

        }

}

/**

 *

 * @Function: FuseArithmetic.java

 * @Description: 当前请求数量大fuseLimit,强制熔断

 * fasle 没有触发限制,true 触发限制

 * @param: fuseStrategy

 * @return:boolean

 *

 * @author: ysf

 * @date: 2021年7月30日 下午8:06:49

 *

 */

public static boolean requestLimitArithmetic(FuseStrategy fuseStrategy){

        if(fuseStrategy.getCurrcentRequestLimit().incrementAndGet()>=fuseStrategy.getBucketLimit()){

                fuseStrategy.getCurrcentRequestLimit().decrementAndGet();

                return true;

         }else {

                return false;

        }

}

/**

 *

 * @Function: FuseArithmetic.java

 * @Description: 释放当前请求资源

 * 漏桶,令牌桶,强制次数限流可释放资源,固定时间窗口不需要放资源

 * @param: fuseStrategy

 * @return:void

 *

 * @author: ysf

 * @date: 2021年7月30日 下午7:45:49

 *

 */

public static void doReleaseResource(FuseStrategy fuseStrategy){

        long releaseNum = 0;

        if(fuseStrategy.getFuseStrategy()==FuseStrategyEnum.FUSE_TOKEN_BUCKET_ALGORITHM.getIndex()){

                releaseNum = fuseStrategy.getCurrcentPushNum().incrementAndGet();

                logger.debug(fuseStrategy.getValue()+"_"+FuseStrategyEnum.FUSE_TOKEN_BUCKET_ALGORITHM.getName()+"_释放1个任务资源_现持有任务资源"+releaseNum+"个");

        }else if(fuseStrategy.getFuseStrategy()==FuseStrategyEnum.FUSE_BUCKET_ALGORITHM.getIndex()

        ||fuseStrategy.getFuseStrategy()==FuseStrategyEnum.FUSE_LIMIT_ALGORITHM.getIndex()){

                releaseNum = fuseStrategy.getCurrcentRequestLimit().decrementAndGet();

                logger.debug(fuseStrategy.getValue()+"_"+FuseStrategyEnum.FUSE_TOKEN_BUCKET_ALGORITHM.getName()+"_释放1个任务资源_现持有任务资源"+releaseNum+"个");

        }

    }

}

熔断限流淘汰策略

public enum FuseEliminateStrategyEnum {

ELIMINATE_RETURN_NULL(0,"返回NULL"),

ELIMINATE_RETURN_ERROR(1,"抱出异常"),

ELIMINATE_RETURN_CAS(2,"乐观锁等待");

private int index;

private String name;

public int getIndex() {

        return index;

}

public String getName() {

        return name;

}

private FuseEliminateStrategyEnum(int index,String name) {

        this.index = index;

        this.name = name;

}

}

熔断限流切面实现->淘汰策略实现FuseStrategyAspect.doAround(ProceedingJoinPoint)

实现部分代码

......

if(fuseStrategy.getEliminateStrategy()==         FuseEliminateStrategyEnum.ELIMINATE_RETURN_NULL.getIndex()){

        logger.debug("并发限流触发,结束返回null");

        return null;

}else if(fuseStrategy.getEliminateStrategy()==         FuseEliminateStrategyEnum.ELIMINATE_RETURN_ERROR.getIndex()){

        logger.debug("并发限流触发,抛出【Throwable】");

        throw new Throwable("系统并发触达上限报错");

}else{

        logger.info("并发限流触发,while(cas)轮询等待资源释放");

        while(checkFuseLimit(fuseStrategy));

        Object res =obj.proceed();

        FuseArithmetic.doReleaseResource(fuseStrategy);

        return res;

}

......

系统接入

POM.xml配置

pom.xml导入jar

<dependency>

        <groupId>fills.tools</groupId>

        <artifactId>fills-fuse-tools</artifactId>

       <version>0.0.1-SNAPSHOT</version>

</dependency>

Spring注入

注解方式

applicationXXX.xml 文件

<context:component-scan base-package="fills.tools.aspect">

        <context:exclude-filter expression="org.springframework.stereotype.Controller" type="annotation" />

</context:component-scan>

手动注入

applicationXXX.xml 文件

<bean id="fuseStrategyAspect" class="fills.tools.aspect.FuseStrategyAspect"/>

SpringBoot注入

启动主函数 StartXXX.java 类头部注解

@SpringBootApplication(exclude = DataSourceAutoConfiguration.class, scanBasePackages = {"com.xxx.xx", "fills.tools.aspect"})

接口层接入

漏桶注解参数

value="doSendEmail" ,限流接口唯一标识;fuseStrategy = 1 限流策略漏桶算法;bucketLimit = 20 ,限流桶容量20;bucketCustomeRate =201秒内消耗任务量;eliminateStrategy = 0熔断策略结束返回null;

@CurrentLimitFuseStrategy(value="doSendEmail",fuseStrategy = 1,bucketLimit = 20,bucketCustomeRate =20,eliminateStrategy = 0)

@Override

public void doSendEmail() {

System.out.println("业务实现忽略");

}

令牌桶注解参数

value="doSendEmail" ,限流接口唯一标识;fuseStrategy = 0 限流策略令牌桶算法;bucketLimit = 20 ,限流桶容量20;bucketPushRate =201秒内生成任务量;eliminateStrategy = 0熔断策略结束返回null;

@CurrentLimitFuseStrategy(value="doSendEmail",fuseStrategy = 0,bucketLimit = 20,bucketPushRate =20,eliminateStrategy = 0)

@Override

public void doSendEmail() {

System.out.println("业务实现忽略");

}

活动窗口注解参数

value="doSendEmail" ,限流接口唯一标识;fuseStrategy = 2 限流策略滑动窗口算法;fixedWaitTime=20等待20毫秒滑动,默认一次;eliminateStrategy = 2熔断策略 while(cas)自旋等待资源释放;

@CurrentLimitFuseStrategy(value="doSendEmail",fuseStrategy = 2,

fixedWaitTime=20,eliminateStrategy = 2)

@Override

public void doSendEmail() {

System.out.println("业务实现忽略");

}

强制限流注解参数

value="doSendEmail" ,限流接口唯一标识;fuseStrategy = 3 限流策略强制算法;bucketLimit =20当前并发限制20次数;eliminateStrategy = 0熔断策略结束返回null;

@CurrentLimitFuseStrategy(value="doSendEmail",fuseStrategy = 2,

bucketLimit = 20,eliminateStrategy = 0)

@Override

public void doSendEmail() {

System.out.println("业务实现忽略");

}

接入JAR

熔断限流切面工具-jar

熔断现楼源码

熔断限流切面工具源码-rar

 


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值