CGLIB+Hystrix 动态生成熔断器

博客介绍了Hystrix熔断功能的实现,当对动态新增接口实现熔断时,可使用动态生成HystrixCommand子类替代注解。采用CGLIB动态生成代理类,还可使用ASM。给出了自定义HystrixCommand及创建动态生成实现类方法的代码,用ConcurrentHashMap提高效率。

一 简介

Hystrix 常用的实现方式为接口上添加注解@HystrixCommand(spring和非spring工程都有实现方式,详细方式),但这种方式在编译期就需要确定接口,而且对于一些工程,接口并不是依靠编码实现的,比如一些SOA可以动态注册接口,这种情况下当对动态新增的接口需要实现hystrix熔断功能时,就可以使用动态生成HystrixCommand子类的方式替代注解。

这里用到的动态生成代理类技术是CGLIB,之所以用CGLIB是因为其实现原理就是通过继承被代理类的方式(实现Hystrix功能需要继承HystrixCommand),而JDKProxy原理是实现接口,也可以使用ASM。

 

二 实现代码

  1. 自定义一个HystrixCommand

    该自定义HystrixCommand主要用于作为动态生成子类的基础父类(主要是为了定义构造器、一些参数和重写一些方法)

    

package com.netflix.hystrix;

import com.google.common.base.Stopwatch;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;

/**
 * @author liguosheng
 * @title: DynamicApiHystrixCommand
 * @projectName demo
 * @description: 自定义的commond,注意该类包名必须是com.netflix.hystrix,因为在使用动态代理执行HystrixCommand
 * 父类方法时,会访问到protected作用域的类,否则会出现访问受限的异常
 * @date 2020/11/3011:52
 */

public class DynamicApiHystrixCommand extends HystrixCommand<String> {
    private final String name;
    //自定义参数的构造方法,用来设置参数,参数的值
    public DynamicApiHystrixCommand(String name) {
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("DynamicCommand"))
                .andCommandKey(HystrixCommandKey.Factory.asKey(name))
                .andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
                .withCircuitBreakerEnabled(true)
                .withCircuitBreakerSleepWindowInMilliseconds(5000)//开启断路器后多长时间后进入半打开状态允许接收少量请求
                .withFallbackEnabled(true)
                .withMetricsRollingPercentileEnabled(true)//带RollingPercentile字样的表示百分比计算参数
                .withMetricsRollingPercentileWindowInMilliseconds(60000)
                .withCircuitBreakerErrorThresholdPercentage(50)//百分比数值
                .withMetricsRollingPercentileWindowBuckets(6)
                .withMetricsRollingPercentileBucketSize(100)
                .withMetricsHealthSnapshotIntervalInMilliseconds(500)
                .withCircuitBreakerRequestVolumeThreshold(20)));//前提条件,达到20个开始计算,否则断路器不会开启
        this.name = name;
    }


    @Override
    protected String run() throws Exception {
        //直接抛出异常用来测试断路器是否开启

        throw new Exception("Error"+name);
    }
    @Override
    public String getFallback(){
        //重写父类方法,用来做执行异常或开启断路器后的服务降级
        Throwable error = getFailedExecutionException();
        //dosomething with error
        return "fallBack:"+name;
    }

    public static void main(String[] args) throws ExecutionException, InterruptedException {
        Stopwatch timer = Stopwatch.createStarted();
        for(int i=0;i<100;i++){
            Future<String> s = new DynamicApiHystrixCommand("ligs"+i).queue();
            System.out.println(s.get());
        }
        timer.stop();
        System.out.println("Within time :"+timer.elapsed(TimeUnit.MILLISECONDS));
    }
}

 备注:构造器中定义的Hystrix参数也可以通过构造器方法或其他方式在运行时确定,包括熔断器打开后执行的逻辑也可以重写,使用动态参数,示例是用于测试所以没有多余逻辑。

 

  2. 创建动态生成hystrixCommon实现类方法

package com.example.demo;

import com.google.common.base.Stopwatch;
import com.netflix.hystrix.DynamicApiHystrixCommand;
import com.netflix.hystrix.contrib.javanica.command.AbstractHystrixCommand;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;


import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

/**
 * @author liguosheng
 * @title: DynamicObjectCreator
 * @projectName demo
 * @description: 动态类生成
 * @date 2020/11/3016:25
 */
public class DynamicObjectCreator implements MethodInterceptor {
    private static ConcurrentHashMap<String,Class<DynamicApiHystrixCommand>> map = new ConcurrentHashMap(16);

    public static <T> T newInstance(String apiName,Class<? extends DynamicApiHystrixCommand> clazz) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
        if(map.containsKey(apiName)){
            System.out.println("Already generator Command Class by apiName:"+apiName);
            return (T) map.get(apiName).getDeclaredConstructor(new Class[]{String.class}).newInstance(new Object[]{apiName});
        }
        System.out.println("start to generator Command Class by apiName:"+apiName);

        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(DynamicApiHystrixCommand.class);
        enhancer.setCallback(new DynamicObjectCreator());
        enhancer.setClassLoader(AbstractHystrixCommand.class.getClassLoader());

        T o = (T) enhancer.create(new Class[]{String.class},new Object[]{apiName});
        map.put(apiName, (Class<DynamicApiHystrixCommand>) o.getClass());
        return o;
    }
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        String name = method.getName();
        if("run".equals(name)){
            //dosomething
        }
        System.out.println("intercept method:"+name);
        return methodProxy.invokeSuper(o,objects);
    }

    public static void main(String[] args) throws InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException, ExecutionException, InterruptedException {
        AtomicInteger errorNum = new AtomicInteger();
        AtomicInteger ShortCircuitNum = new AtomicInteger();
        Stopwatch sw = Stopwatch.createStarted();
        for(int i=0;i<100;i++){
            //根据cglib代理动态生成一个commond类(每个接口方法对应一个,做接口的熔断器)
            DynamicApiHystrixCommand commond = DynamicObjectCreator.newInstance("ligs1", DynamicApiHystrixCommand.class);
            Future future = commond.queue();
            future.get();
            if(commond.isResponseShortCircuited()){
                ShortCircuitNum.incrementAndGet();
            }
            if(commond.isFailedExecution()){
                errorNum.incrementAndGet();
            }
            Thread.sleep(50);
        }
        sw.stop();
        System.out.println("finish time cost :"+sw.elapsed(TimeUnit.MILLISECONDS));
        System.out.println("errorNum"+errorNum.get());
        System.out.println("ShortCircuitNum:"+ShortCircuitNum.get());
    }
}

  备注:使用ConcurrentHashMap存放apiName对应的动态生成的HystrixCommand实现类的class,提高效率。

详细Hystrix参数传送门详情

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值