Reactive Commands
您可以创建一个HystrixObservableCommand,它是HystrixCommand的专用版本,用于包装Observables,而不是使用上述方法将HystrixCommand转换为Observable。HystrixObservableCommand能够包装发出多个项目的Observable,而普通的HystrixCommands,即使转换为Observables,也不会发出多个项目。
在这种情况下,不要使用命令逻辑覆盖run方法(就像使用普通的HystrixCommand一样),而是覆盖构造方法,以便返回要包装的Observable。
要获取HystrixObservableCommand的Observable表示,请使用以下两种方法之一:
- observe() - 返回一个“热”的Observable,它立即订阅底层的Observable,但是因为它是通过ReplaySubject过滤的,所以在你有机会订阅生成的Observable之前,你不会丢失它发出的任何项目。
- toObservable() - 返回一个“冷”Observable,在您订阅生成的Observable之前,它不会订阅底层的Observable。
Fallback
您可以通过添加Hystrix将调用的回退方法来支持Hystrix命令中的优雅降级,以便在主命令失败时获取默认值。您可能希望为大多数可能会失败的Hystrix命令实现回退,但有几个例外:
- 执行写操作的命令
- 如果你的Hystrix命令被设计为执行写操作而不是返回一个值(这样的命令通常在HystrixCommand的情况下返回一个空格,或者在HystrixObservableCommand的情况下返回一个空的Observable),没有多少意义实施后备。如果写入失败,您可能希望失败传播回调用者。
- 批处理系统/离线计算
- 如果您的Hystrix命令正在填充缓存,生成报告或进行任何类型的离线计算,通常更适合将错误传播回调用者,然后调用者可以稍后重试该命令,而不是将调用者发送给调用者。无声的退化反应。
无论您的命令是否具有回退,都会更新所有常见的Hystrix状态和断路器状态/指标,以指示命令失败。在普通的HystrixCommand中,您可以通过getFallback()实现实现回退。
Hystrix将针对所有类型的故障执行此回退,例如run()失败,超时,线程池或信号量拒绝以及断路器短路。
以下示例包含此类后备:
public class CommandHelloFailure extends HystrixCommand<String> {
private final String name;
public CommandHelloFailure(String name) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.name = name;
}
@Override
protected String run() {
throw new RuntimeException("this command always fails");
}
@Override
protected String getFallback() {
return "Hello Failure " + name + "!";
}
}
每次执行时,此命令的run()方法都将失败。但是,调用者将始终接收命令的getFallback()方法返回的值,而不是接收异常:
@Test
public void testSynchronous() {
assertEquals("Hello Failure World!", new CommandHelloFailure("World").execute());
assertEquals("Hello Failure Bob!", new CommandHelloFailure("Bob").execute());
}
HystrixObservableCommand
Equivalent
对于HystrixObservableCommand,您可以覆盖resumeWithFallback方法,以便它返回第二个Observable,如果它失败,它将从主Observable接管。请注意,因为Observable在已经发出一个或多个项目后可能会失败,所以您的回退不应该假设它将发出观察者将看到的唯一值。
在内部,Hystrix使用RxJava onErrorResumeNext运算符在发生错误时在主要和回退Observable之间无缝转换。
Error Propagation
从run()方法抛出的所有异常除了HystrixBadRequestException都计为失败并触发getFallback()和断路器逻辑。
您可以在HystrixBadRequestException中包装要抛出的异常,并通过getCause()检索它。HystrixBadRequestException适用于报告非法参数或非系统故障等用例,这些故障不应计入故障指标,也不应触发回退逻辑。
HystrixObservableCommand
Equivalent
在HystrixObservableCommand的情况下,通过来自结果Observable的onError通知返回不可恢复的错误,并通过回退到Hystrix通过您实现的resumeWithFallback方法获得的第二个Observable来完成回退。
Execution Exception types
Failure Type | Exception class | Exception.cause | subject to fallback |
---|---|---|---|
FAILURE | HystrixRuntimeException | underlying exception (user-controlled) | YES |
TIMEOUT | HystrixRuntimeException | j.u.c.TimeoutException | YES |
SHORT_CIRCUITED | HystrixRuntimeException | j.l.RuntimeException | YES |
THREAD_POOL_REJECTED | HystrixRuntimeException | j.u.c.RejectedExecutionException | YES |
SEMAPHORE_REJECTED | HystrixRuntimeException | j.l.RuntimeException | YES |
BAD_REQUEST | HystrixBadRequestException | underlying exception (user-controlled) | NO |
Command Name
默认情况下,命令名称是从类名派生的:
getClass().getSimpleName();
要显式定义名称,请通过HystrixCommand或HystrixObservableCommand构造函数传递它:
public CommandHelloWorld(String name) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld")));
this.name = name;
}
要保存每个命令分配的Setter分配,您也可以像这样缓存Setter:
private static final Setter cachedSetter =
Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"));
public CommandHelloWorld(String name) {
super(cachedSetter);
this.name = name;
}
HystrixCommandKey是一个接口,可以实现为枚举或常规类,但它也有辅助工具类来构造和实例实例,例如:
HystrixCommandKey.Factory.asKey("HelloWorld")
Command Group
Hystrix使用命令组密钥将命令组合在一起,例如报告,警报,仪表板或团队/库所有权。
默认情况下,Hystrix使用它来定义命令线程池,除非定义了单独的线程池。
HystrixCommandGroupKey是一个接口,可以实现为枚举或常规类,但它也有辅助工具类来构造和实例实例,例如:
HystrixCommandGroupKey.Factory.asKey("ExampleGroup")
Command Thread-Pool
线程池密钥表示用于监视,度量标准发布,缓存和其他此类用途的HystrixThreadPool。HystrixCommand与注入其中的HystrixThreadPoolKey检索的单个HystrixThreadPool相关联,或者默认为使用创建的HystrixCommandGroupKey创建的HystrixThreadPool。
要显式定义名称,请通过HystrixCommand或HystrixObservableCommand构造函数传递它:
public CommandHelloWorld(String name) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")));
this.name = name;
}
HystrixThreadPoolKey是一个接口,可以实现为枚举或常规类,但它也有辅助工具类来构造和实例实例,例如:
HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")
您可能使用HystrixThreadPoolKey而不仅仅是不同的HystrixCommandGroupKey的原因是多个命令可能属于相同的“所有权或逻辑功能”组,但某些命令可能需要彼此隔离。
这是一个简单的例子:
- 用于访问视频元数据的两个命令
- 组名是“VideoMetadata”
- 命令A与资源#1相反
- 命令B与资源#2相反
如果命令A变为潜在的并且使其线程池饱和,则它不应该阻止命令B执行请求,因为它们每个都命中不同的后端资源。
因此,我们逻辑上希望将这些命令组合在一起,但希望它们以不同方式隔离,并使用HystrixThreadPoolKey为它们中的每一个提供不同的线程池。