原创不易,如若转载 请标明来源!
前言
上一个系列文章讲解了Feign的源码,主要是Feign动态代理实现的原理,及配合Ribbon实现负载均衡的机制。
这里我们讲解一个新的组件Hystrix,也是和Feign进行融合的。
这一讲开始讲解Hystrix相关代码,当然还是基于上一个组件Feign的基础上开始讲解的,这里默认你对Feign已经有了大致的了解。
使用过spring cloud的小伙伴对这个组件都不会陌生,Hystrix是保证系统高可用一个很重要的组件,主要提供一下几个功能:
目录如下:
将所有请求外部系统(或者叫依赖服务)的逻辑封装到 HystrixCommand 或者 HystrixObservableCommand 对象中。
Run()方法为实现业务逻辑,这些逻辑将会在独立的线程中被执行当请求依赖服务时出现拒绝服务、超时或者短路(多个依赖服务顺序请求,前面的依赖服务请求失败,则后面的请求不会发出)时,执行该依赖服务的失败回退逻辑(Fallback)。
Hystrix 为每个依赖项维护一个小线程池(或信号量);如果它们达到设定值(触发隔离),则发往该依赖项的请求将立即被拒绝,执行失败回退逻辑(Fallback),而不是排队。
隔离策略分线程隔离和信号隔离。
线程池的缺点
熔断机制是一种保护性机制,当系统中某个服务失败率过高时,将开启熔断器,对该服务的后续调用,直接拒绝,进行Fallback操作。
熔断所依靠的数据即是Metrics中的HealthCount所统计的错误率。
如何判断是否应该开启熔断器?
必须同时满足两个条件:
当construct()或run()执行失败时,Hystrix调用fallback执行回退逻辑,回退逻辑包含了通用的响应信息,这些响应从内存缓存中或者其他固定逻辑中得到,而不应有任何的网络依赖。
如果一定要在失败回退逻辑中包含网络请求,必须将这些网络请求包装在另一个 HystrixCommand 或 HystrixObservableCommand 中,即多次降级。
失败降级也有频率限时,如果同一fallback短时间请求过大,则会抛出拒绝异常。
同一对象的不同HystrixCommand实例,只执行一次底层的run()方法,并将第一个响应结果缓存起来,其后的请求都会从缓存返回相同的数据。
由于请求缓存位于construct()或run()方法调用之前,所以,它减少了线程的执行,消除了线程、上下文等开销。
用一张简单地流程图总结:
Demo工程还是使用之前的项目,git地址:https://github.com/barrywangmeng/spring-cloud-learn
eureka-server:注册中心 serviceA: 提供对外接口 serviceB: 通过feign调用serviceA接口
在serviceB项目中添加hystrix相关pom依赖及配置,这里就不列出来了,小伙伴们可以直接下载这个项目看一下。
接着就是改造对serviceA调用的FeignClient:
我们可以调整serviceB中feign调用超时时间配置类模拟触发Hystrix降级逻辑:
我们在调试的过程中,为了方便走正常不降级逻辑的debug调试,特地会修改feign及hystrix的超时时间。
因为hystrix中大量使用了响应式编程(rxJava),代码中包含大量的观察者模式设计,各种回调函数糅杂在一起,所以代码显得很难懂。
这里我们不纠结更多的rxJava源码,为了调试,每个回调方法都会打上断点。
关于Hystrix daboard相关的内容这里也不会讲解,实际项目中会使用其他第三方组件来做服务监控,这里不做更多研究。
之前我们讲过,如果不配置feign.hystrix.enabled:true这个配置的话,默认用的是DefaultTargeter
, 配置了的话就改变为HystrixTargeter
。
我们来看看HystrixTargeter.target()
方法:
class HystrixTargeter implements Targeter {
@Override
public <T> T target(FeignClientFactoryBean factory, Feign.Builder feign, FeignContext context,
Target.HardCodedTarget<T> target) {
if (!(feign instanceof feign.hystrix.HystrixFeign.Builder)) {
return feign.target(target);
}
// 里面包含encoder、decoder等feign的组件信息
feign.hystrix.HystrixFeign.Builder builder = (feign.hystrix.HystrixFeign.Builder) feign;
// factory.getName: serviceA 返回的setterFactory 是null
SetterFactory setterFactory = getOptional(factory.getName(), context,
SetterFactory.class);
if (setterFactory != null) {
builder.setterFactory(setterFactory);
}
// 获取设置的feignClient的fallback属性
Class<?> fallback = factory.getFallback();
if (fallback != void.class) {
return targetWithFallback(factory.getName(), context, target, builder, fallback);
}
// 获取设置的feignClient的fallbackFactory属性
Class<?> fallbackFactory = factory.getFallbackFactory();
if (fallbackFactory != void.class) {
// 配置了降级factory的话,直接进入这个逻辑
return targetWithFallbackFactory(factory.getName(), context, target, builder, fallbackFactory);
}
return feign.target(target);
}
private <T> T targetWithFallbackFactory(String feignClientName, FeignContext context,
Target.HardCodedTarget<T> target,
HystrixFeign.Builder builder,
Class<?> fallbackFactoryClass) {
FallbackFactory<? extends T> fallbackFactory = (FallbackFactory<? extends T>)
getFromContext("fallbackFactory", feignClientName, context, fallbackFactoryClass, FallbackFactory.class);
// 调用我们自定义的fallback工厂中的create方法
Object exampleFallback = fallbackFactory.create(new RuntimeException());
Assert.notNull(exampleFallback,
String.format(
"Incompatible fallbackFactory instance for feign client %s. Factory may not produce null!",
feignClientName));
// target.type() 就是ServiceAFeignClient 这个feignClient接口的名称 这里就是做些判断
if (!target.type().isAssignableFrom(exampleFallback.getClass())) {
throw new IllegalStateException(
String.format(
"Incompatible fallbackFactory instance for feign client %s. Factory produces instances of '%s', but should produce instances of '%s'",
feignClientName, exampleFallback.getClass(), target.type()));
}
// 执行HystrixFeign中的target方法
return builder.target(target, fallbackFactory);
}
}
我们设置的这个FallbackFactory负责在每次超时、拒绝(线程池满)、异常的时候,create()方法返回一个降级机制的对象
从服务(ServiceA)的独立的spring容器中取出来一个独立的FallbackFactory,调用每个服务的时候,他对应的FallbackFactory都是存在于那个服务关联的独立的spring容器中的。
接着进入到Hystrix.target()
中:
public final class HystrixFeign {
public static Builder builder() {
return new Builder();
}
public static final class Builder extends Feign.Builder {
private Contract contract = new Contract.Default();
private SetterFactory setterFactory = new SetterFactory.Default();
/**
* @see #target(Class, String, FallbackFactory)
*/
public <T> T target(Target<T> target, FallbackFactory<? extends T> fallbackFactory) {
return build(fallbackFactory).newInstance(target);
}
Feign build(final FallbackFactory<?> nullableFallbackFactory) {
super.invocationHandlerFactory(new InvocationHandlerFactory() {
@Override public InvocationHandler create(Target target,
Map<Method, MethodHandler> dispatch) {
// 设置invocationHandlerFactory为HystrixInvocationHandler
return new HystrixInvocationHandler(target, dispatch, setterFactory, nullableFallbackFactory);
}
});
// 设置contact为HystrixDelegatingContract
super.contract(new HystrixDelegatingContract(contract));
// 调用父类的build方法
return super.build();
}
}
}
public class ReflectiveFeign extends Feign {
public <T> T newInstance(Target<T> target) {
Map<String, MethodHandler> nameToHandler = targetToHandlersByName.apply(target);
Map<Method, MethodHandler> methodToHandler = new LinkedHashMap<Method, MethodHandler>();
List<DefaultMethodHandler> defaultMethodHandlers = new LinkedList<DefaultMethodHandler>();
for (Method method : target.type().getMethods()) {
if (method.getDeclaringClass() == Object.class) {
continue;
} else if(Util.isDefault(method)) {
DefaultMethodHandler handler = new DefaultMethodHandler(method);
defaultMethodHandlers.add(handler);
methodToHandler.put(method, handler);
} else {
methodToHandler.put(method, nameToHandler.get(Feign.configKey(target.type(), method)));
}
}
// 和之前一样,生成一个JDK动态代理对象
InvocationHandler handler = factory.create(target, methodToHandler);
T proxy = (T) Proxy.newProxyInstance(target.type().getClassLoader(), new Class<?>[]{target.type()}, handler);
for(DefaultMethodHandler defaultMethodHandler : defaultMethodHandlers) {
defaultMethodHandler.bindTo(proxy);
}
return proxy;
}
}
最终实际用来去处理这个请求的,其实是InvocationHandler,他是JDK动态代理的核心,基于JDK动态代理机制,生成一个动态代理的对象之后,对这个对象所有的方法调用,都会走关联的那个InvocationHandler。
我们这里设置的是HystrixInvocationHandler
,来看下它的构造参数:
接下来还设置了contract信息,Contract是解析第三方注解的组件,设置为了HystrixDelegatingContract,顾名思义,就是说,设置了这个组件之后,后面就可以解析你在各个接口上hystrix相关的一些注解。
上面已经分析了Hystrix基础原理与Demo的搭建,基础原理中用一张简单地图画了Hystrix实现的流程,后面会更加详细的依据这个图进行讲解。
本文章首发自本人博客:https://www.cnblogs.com/wang-meng 和公众号:壹枝花算不算浪漫,如若转载请标明来源!