通过Netflix Hystrix官方公布的流程图,我们来了解一下Hystrix的工作流程。
1.创建HystrixCommand对象或者HystrixObservableCommand对象
首先创建一个HystrixCommand对象或者HystrixObservableCommand对象用来表示对依赖服务的操作请求,同时传递所有需要的参数,它采用了命令模式来实现对服务调用操作的封装,这两个Command对象分别针对不同的应用场景。
HystrixCommand command = new HystrixCommand(arg1, arg2);
HystrixObservableCommand command = new HystrixObservableCommand(arg1, arg2);
命令模式
关于命令模式,将请求封装成对象,使用不同的请求、队列,或者日志请求来参数化其他对象,就是命令模式。命令模式也可以支持撤销操作。当需要将发出请求的对象和执行请求的对象解耦的时候,可以考虑使用命令模式。
举个例子,什么是命令,比如说打开灯就是一个命令,我们可以将这个命令命名为LightOnCommand,上面说的将请求封装成对象的意思就是说将打开灯这个请求(命令),封装成LightOnCommand这一对象,但是这个LightOnCommand命令肯定不能自己执行,要确定是谁被执行这个命令,而被执行这个命令的对象就是灯,此时可以把这个灯,比如说节能灯CellingLight对象注入到命令中,在LightOnCommand命令对象中添加一个execute()方法,用来执行light.on()(打开灯)的操作。上面说了命令对象、被执行者(或者叫被命令者)对象,还差一个执行命令的对象,这里可以是一个遥控器,将LightOnCommand对象注入进来,在执行命令的时候,我们就可以直接调用这个命令对象就好了,而不需要知道我的这个命令到底是哪一个具体的灯去执行,这样就达到了程序上的解耦。当然我们除了存在execute()方法做为下一步执行的操作方法,还可以提供一个undo()的撤销方法。
命令模式的要点:
2.命令执行
从图中看到一共有四种命名的执行方式,其中HystrixCommand实现了这四个执行方式,但主要是使用前两个:
R value = command.execute();
Future fValue = command.queue();
HystrixObservableCommand实现了另外两种执行方式。
Observable ohValue = command.observe(); //hot observable
Observable ocValue = command.toObservable(); //cold observable
在Hystrix的底层实现中大量使用了RxJava,这里简单介绍一下RxJava的观察者-订阅者模式。
Observable对象可以理解为事件源或者被观察者,与之对应的是Subscriber对象,可以理解为订阅者或者观察者。
下面是一个简单的例子
public static void main(String[] args) {
//创建被观察者/事件源observable
Observable observable = Observable.create(new Observable.OnSubscribe() {
@Override
public void call(Subscriber subscriber) {
// TODO Auto-generated method stub
subscriber.onNext("hello!RxJava");
subscriber.onNext("hello!Spring Cloud Hystrix");
subscriber.onCompleted();
}
});
//创建观察者/订阅者subscriber
Subscriber subscriber = new Subscriber(){
@Override
public void onCompleted() {
// TODO Auto-generated method stub
System.out.println("success");
}
@Override
public void onError(Throwable e) {
// TODO Auto-generated method stub
System.out.println("fail");
}
@Override
public void onNext(String t) {
// TODO Auto-generated method stub
System.out.println(t);
}
};
observable.subscribe(subscriber);
}
在该示例中,创建了一个简单的事件源observable,一个对事件传递内容输出的订阅者subscriber,通过observable.subscribe(subscriber)来触发事件的发布。
关于Hot Observable和Cold Observable,其中Hot Observable不论事件源是否有订阅者都会在创建后对事件进行发布,所以对于Hot Observable的每一个订阅者都有可能是从事件源的中途开始的,并且只能看到整个操作的局部过程;而Cold Observable在没有订阅者的时候不会发布事件,而是进行等待,直到有订阅者之后才发布事件,所以对于Cold Observable的订阅者,它可以保证从一开始看到整个操作的全部过程。
实际上不止observe()和toObservable()使用了RxJava,execute()和queue()也都使用RxJava来实现。
public R execute() {
try {
return queue().get();
} catch (Exception e) {
throw Exceptions.sneakyThrow(decomposeException(e));
}
}
//
public Future queue() {
final Future delegate = toObservable().toBlocking().toFuture();
final Future f = new Future() {
...
};
if (f.isDone()) {
try {
f.get();
return f;
} catch (Exception e) {
...
}
}
return f;
}
3.结果是否被缓存
若当前命令的请求缓存功能是被启用的,并且该命令缓存命中,那么缓存的结果会立即以Observable对象的形式返回。
4.断路器是否打开
在命令结果没有被缓存命中的时候,Hystrix会在执行命令前检查断路器是否为打开状态
5.线程池、请求队列、信号量是否占满
如果与命令相关的线程池和请求队列或者信号量(不使用线程池的时候)已经被占满,那么Hystrix不会执行命令,而是转接到fallback处理逻辑(第8步)。注意,这个线程池不是容器的线程池而是Hystrix为了保证不会因为某个依赖服务的问题影响到其他依赖服务而采用了“舱壁模式”来隔离每个依赖的服务。
6.HystrixObservableCommand.construct()或HystrixCommand.run()
Hystrix会根据我们编写的方法来决定采取什么方式去请求依赖服务。
如果run()或construct()方法执行时长超过了命令的超时阀值,其线程将抛出一个TimeoutException(或者在一个单独的线程抛出,如果命令没有运行在它自己的线程)。这种情况下 Hystrix转接到fallback处理逻辑(第8步)。并且如果该命令没有取消或中断,它将放弃run()或construct()方法最终的返回值。
如果命令没有抛出异常并且返回了响应,Hystrix 将会在执行一些日志记录和度量报告之后返回结果给调用者。如果是通过run()运行,Hystrix 将返回 Observable 发射单个结果,然后发送一个onCompleted的通知;如果是通过construct()运行,Hystrix 直接返回该方法产生的Observable对象。
7.计算断路器的健康度
Hystrix会将成功、失败、拒绝、超时等信息报告给断路器,而断路器会维护一组计数器来统计这些数据。断路器会使用这些数据确定是否将断路器打开,来对某个依赖服务的请求进行熔断、短路,直到恢复期结束,若恢复期结束后,根据统计数据判断仍未达到健康指标,会再次熔断、短路。
8.fallback处理
当命令执行失败时,Hystrix会进入fallback尝试回退处理,也叫服务降级,可以引入服务降级的请求有下面几种:
在服务降级逻辑中,需要实现一个通用的响应结果,并且该结果的处理逻辑应当是从缓存或是根据一些静态逻辑来获取,而不是依赖网络请求获取,如果一定要在服务降级逻辑中包含网络请求,那么该请求也必须包装在HystrixCommand或HystrixObservableCommand中,从而形成级联的降级策略,而最终的降级逻辑一定不是一个依赖网络请求的处理,而是一个能够稳定的返回结果的处理逻辑。
当命令的降级逻辑返回结果后,Hystrix就将该结果返回给调用者。当使用HystrixCommand.getFallback()的时候,它会返回一个Observable对象,该对象会发射getFallback()的处理结果。而使用HystrixObservableCommand.resumeWithFallback()实现的时候会将Observable对象直接返回。
如果没有为命令实现降级逻辑或者降级逻辑中抛出了异常,Hystrix依然会返回一个Observable对象,但是它不会发射任何结果数据,而是通过onError方法通知命令立即中断请求,并通过onError()方法将异常发送给调用者,我们应该尽可能避免降级出现失败的情况。如果降级策略逻辑执行发生失败,Hystrix会根据不同的执行方式做出以下处理:
9.返回成功的响应
当Hystrix命令执行成功后,会将处理结果直接返回或是以Observable的形式返回,具体怎么返回要根据执行命令的方式来区分,下图是四种执行方式的依赖关系: