以下是 HystrixCommand 的基本“Hello World”实现:
public class CommandHelloWorld extends HystrixCommand<String> {
private final String name;
public CommandHelloWorld(String name) {
super(HystrixCommandGroupKey.Factory.asKey("exampleGroup"));
this.name = name;
}
@Override
protected String run() {
return "hello" + name +"!";
}
}
使用 HystrixObservableCommand 而不是 HystrixCommand 的等效 Hello World 解决方案将涉及重写构造方法,如下所示:
public class ObCommandHelloWorld extends HystrixObservableCommand<String> {
private final String name;
public ObCommandHelloWorld(String name) {
super(HystrixCommandGroupKey.Factory.asKey("exampleGroup"));
this.name = name;
}
@Override
protected Observable<String> construct() {
return Observable.create(new Observable.OnSubscribe<String>() {
@Override
public void call(Subscriber<? super String> subscriber) {
try{
if (!subscriber.isUnsubscribed()){
// 一个真实的例子:像网络调用
subscriber.onNext("Hello");
subscriber.onNext(name+"!");
subscriber.onCompleted();
}
}catch (Exception e){
subscriber.onError(e);
}
}
}).subscribeOn(Schedulers.io());
}
}
同步执行
我们可以使用 execute() 方法同步执行 HystrixCommand,如下例所示:
CommandHelloWorld command = new CommandHelloWorld("jin shuai");
String execute = command.execute();
对于 HystrixObservableCommand 没有简单的等价物执行,但如果我们知道此类命令产生的 Observable 必须始终只产生一个值,我们可以通过将 .toBlocking().toFuture().get() 应用于 Observable 来模仿exexute的行为。
ObCommandHelloWorld command = new ObCommandHelloWorld("js");
String s = command.observe().toBlocking().toFuture().get();
异步执行
我们可以使用 queue() 方法异步执行 HystrixCommand,如下例所示:
CommandHelloWorld command = new CommandHelloWorld("jin shuai");
Future<String> queue = command.queue();
String s = queue.get();
以下是彼此等价的:
String s1 = command.queue().get();
String s2 = command.execute();
HystrixObservableCommand 等效
对于 HystrixObservableCommand 没有简单的队列等价物,但是如果我们知道此类命令生成的 Observable 必须始终只生成一个值,我们可以通过应用 RxJava 运算符 .toBlocking().toFuture( ) 到 Observable。
反应式执行
我们还可以使用以下方法之一将 HystrixCommand 的结果作为 Observable 来观察:
- observe() — 返回一个立即执行命令的“hot” Observable,但由于 Observable 是通过 ReplaySubject 过滤的,所以在我们有机会subscribe之前,我们不会有丢失它发出的任何项目的危险
- toObservable() — 返回一个“cold”的 Observable,它不会执行命令并开始发送结果,直到我们订阅 Observable
Observable<String> hot = new ObCommandHelloWorld("js").observe();
Observable<String> cold = new ObCommandHelloWorld("js").toObservable();
然后通过订阅 Observable 来检索命令的值:
hot.subscribe(new Action1<String>() {
@Override
public void call(String s) {
System.out.println("-------"+s);
}
});
使用 Java 8 lambdas/closures 更紧凑; 它看起来像这样:
hot.subscribe(s1 -> {
System.out.println("-------"+ s1);
});
反应式Commands
除了使用上述方法将 HystrixCommand 转换为 Observable 之外,我们还可以创建一个 HystrixObservableCommand,它是 HystrixCommand 的专用版本,用于包装 Observables。 HystrixObservableCommand 能够包装发出多个项目的 Observables,而普通的 HystrixCommands,即使转换为 Observables,也永远不会发出超过一个项目。
在这种情况下,不要用命令逻辑覆盖 run 方法(就像使用普通 HystrixCommand 一样),而是覆盖构造方法,以便它返回我们打算包装的 Observable。
要获取 HystrixObservableCommand 的 Observable 表示,请使用以下两种方法之一:
- observe() — 返回一个立即订阅底层 Observable 的“hot” Observable,尽管因为它是通过 ReplaySubject 过滤的,所以在我们有机会订阅生成的 Observable 之前,我们不会有丢失它发出的任何项目的危险
- toObservable() — 返回一个“cold”的 Observable,在我们订阅生成的 Observable 之前不会订阅底层的 Observable
FallBack
我们可以通过添加一个fallback方法来支持 Hystrix 命令中的优雅降级,Hystrix 将调用该方法来获取一个或多个默认值,以防主命令失败。 我们将希望为大多数可能会失败的 Hystrix 命令实现回退,但有几个例外:
- 执行写操作的命令
如果我们的 Hystrix 命令旨在执行写入操作而不是返回值(这样的命令通常可能在 HystrixCommand 的情况下返回 void 或在 HystrixObservableCommand 的情况下返回空的 Observable),那么没有多大意义 在实施fallback。 如果写入失败,我们可能希望将失败传播回调用者。
- 批处理系统/离线计算
如果我们的 Hystrix 命令正在填充缓存、生成报告或进行任何类型的离线计算,通常更适合将错误传播回调用者,然后调用者可以稍后重试命令,而不是向调用者发送 默默降级的反应。
无论我们的命令是否有回退,所有常见的 Hystrix 状态和断路器状态/指标都会更新以指示命令失败。
在普通的 HystrixCommand 中,我们通过 getFallback() 实现来实现回退。 Hystrix 将对所有类型的故障执行此回退,例如 run() 故障、超时、线程池或信号量拒绝以及断路器短路。 以下示例包括这样的回退:
public class CommandHelloWorld extends HystrixCommand<String> {
private final String name;
public CommandHelloWorld(String name) {
super(HystrixCommandGroupKey.Factory.asKey("exampleGroup"));
this.name = name;
}
@Override
protected String run() {
throw new RuntimeException("失败");
}
@Override
protected String getFallback() {
return "失败了:"+name;
}
}
HystrixObservableCommand 等效
对于 HystrixObservableCommand,我们可以改写 resumeWithFallback 方法,以便它返回第二个 Observable,如果失败,它将从主 Observable 接管。 请注意,由于 Observable 在发出一个或多个项目后可能会失败,因此我们的fallback不应假定它将发出观察者将看到的唯一值。
在内部,Hystrix 使用 RxJava 的 onErrorResumeNext 操作符在发生错误时在主 Observable 和备用 Observable 之间无缝转换。
错误传播
除了 HystrixBadRequestException 之外,从 run() 方法抛出的所有异常都算作失败并触发 getFallback() 和断路器(circuit-breaker)逻辑。
我们可以将要抛出的异常包装在 HystrixBadRequestException 中并通过 getCause() 检索它。 HystrixBadRequestException 旨在用于报告非法参数或非系统故障等用例,这些故障不应计入故障指标并且不应触发回退(fallback)逻辑。
HystrixObservableCommand 等效
在 HystrixObservableCommand 的情况下,不可恢复的错误是通过来自结果 Observable 的 onError 通知返回的,并且通过回退到 Hystrix 通过我们实现的 resumeWithFallback 方法获得的第二个 Observable 来完成回退。
执行异常类型
Failure Type | Exception class | Exception.cause | subject to fallback |
Failure | HystrixRuntimeException | 底层异常(用户控制) | YES |
Timeout | HystrixRuntimeException | j.u.c.TimeoutException | YES |
short_circuited | HystrixRuntimeException | j.l.TimeoutException | YES |
thread_pool_rejected | HystrixRuntimeException | j.u.c.RejectedExecutionException | YES |
semaphore_rejected | HystrixRuntimeException | j.l.TimeoutException | YES |
bad_request | HystrixBadRequestException | 底层异常(用户控制) | NO |