一 简介
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参数传送门详情

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

被折叠的 条评论
为什么被折叠?



