目录
- Hystrix本系列博文
- 静态降级(返回默认值)
- 自定义降级
- 网络缓存降级
- 主备降级
- 声明
Hystrix本系列博文
以下为博主写Hystrix系列的文章列表
点击查看 Hystrix入门
点击查看 Hystrix命令执行
点击查看 Hystrix处理异常机制(降级方法)
点击查看 Hystrix命令名称、分组、线程池
点击查看 Hystrix命令名称、Hystrix请求处理
点击查看 Hystrix请求处理
点击查看 Hystrix常用场景--失败
静态降级(返回默认值)
可以在代码中静态的返回默认值进行降级, 这不会导致功能或服务以“静默失败”的方式被删除,而是导致默认行为发生。
例如,如果一个命令基于用户凭据返回true/false,如果命令执行失败,它可以默认为true:
@Override
protected Boolean getFallback() {
return true;
}
HystrixObservableCommand
等价
对于 HystrixObservableCommand
的静默失败解决方案是调用重写 resumeWithFallback
方法,示例如下:
@Override
protected Observable<Boolean> resumeWithFallback() {
return Observable.just( true );
}
自定义降级
当命令返回的是一个包含多个字段的复合对象时,通常会使用自定义降级,其中一些字段可以由其他请求状态确定,而其他字段设置为默认值。
适合使用自定义降级的例子有:
- cookies
- 请求参数和请求头
- 之前的服务请求在当前失败之前的响应
降级时会静态的从请求范围检索出存根, 但是如果需要的话,比如下面这个例子演示了它如何处理countryCodeFromGeoLookup字段,通常建议在命令实例化时注入它们。
public class HystrixStubbedFallback extends HystrixCommand<HystrixStubbedFallback.UserAccount> {
private final int customerId;
private final String countryCodeFromGeoLookup;
/**
* @param customerId 用于检索UserAccount
* @param countryCodeFromGeoLookup
* 来自HTTP请求geo代码查找的默认国家代码用于回退。
*/
protected HystrixStubbedFallback(int customerId, String countryCodeFromGeoLookup) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.customerId = customerId;
this.countryCodeFromGeoLookup = countryCodeFromGeoLookup;
}
@Override
protected UserAccount run() {
// 从远程服务获取UserAccount
// 返回 UserAccountClient.getAccount(customerId);
throw new RuntimeException("forcing failure for example"); //模拟异常用于触发回滚
}
@Override
protected UserAccount getFallback() {
/**
* 返回有一些静态默认值,占位符,还有一个注入的我们将使用的值'countryCodeFromGeoLookup'。
* 而不是我们从远程服务检索出来的
*/
return new UserAccount(customerId, "Unknown Name",
countryCodeFromGeoLookup, true, true, false);
}
public static class UserAccount {
private final int customerId;
private final String name;
private final String countryCode;
private final boolean isFeatureXPermitted;
private final boolean isFeatureYPermitted;
private final boolean isFeatureZPermitted;
UserAccount(int customerId, String name, String countryCode,
boolean isFeatureXPermitted,
boolean isFeatureYPermitted,
boolean isFeatureZPermitted) {
this.customerId = customerId;
this.name = name;
this.countryCode = countryCode;
this.isFeatureXPermitted = isFeatureXPermitted;
this.isFeatureYPermitted = isFeatureYPermitted;
this.isFeatureZPermitted = isFeatureZPermitted;
}
}
}
下面是单元测试示范上面代码的功能:
@Test
public void test() {
CommandWithStubbedFallback command = new CommandWithStubbedFallback(1234, "ca");
UserAccount account = command.execute();
assertTrue(command.isFailedExecution());
assertTrue(command.isResponseFromFallback());
assertEquals(1234, account.customerId);
assertEquals("ca", account.countryCode);
assertEquals(true, account.isFeatureXPermitted);
assertEquals(true, account.isFeatureYPermitted);
assertEquals(false, account.isFeatureZPermitted);
}
HystrixObservableCommand
等价
对于 HystrixObservableCommand
的静默失败解决方案是调用重写 resumeWithFallback
方法, 用于返回一个可观察者,发送自一定的响应。与前面的例子等价的版本是这样的:
@Override
protected Observable<Boolean> resumeWithFallback() {
return Observable.just( new UserAccount(customerId, "Unknown Name",
countryCodeFromGeoLookup, true, true, false) );
}
但是,如果您期望从 Observable
中发出多个数据项,您可能更感兴趣的是为那些在失败前还没发射原始的 Observable
生成存根。这里有一个简单的例子来说明如何实现这一点——跟踪主Observable
中发出的最后一个数据项,从而使回退知道在何处继续这个顺序:
@Override
protected Observable<Integer> construct() {
return Observable.just(1, 2, 3)
.concatWith(Observable.<Integer> error(new RuntimeException("forced error")))
.doOnNext(new Action1<Integer>() {
@Override
public void call(Integer t1) {
lastSeen = t1;
}
})
.subscribeOn(Schedulers.computation());
}
@Override
protected Observable<Integer> resumeWithFallback() {
if (lastSeen < 4) {
return Observable.range(lastSeen + 1, 4 - lastSeen);
} else {
return Observable.empty();
}
}
通过网络缓存降级
有时如果后端服务失败,可以从缓存服务(如memcached)检索过时的数据版本。 由于回退会超出网络,所以它是另一个可能的失败点,因此它也需要被一个HystrixCommand或HystrixObservableCommand包裹。
(执行示意图)
在单独的线程池上执行降级(回退)命令是很重要的,否则,如果主命令被隐藏并填充线程池,因为两个命令共享同一个池,将阻止降级(回退)。
下面将举例怎样使用 CommandWithFallbackViaNetwork
中的 getFallback()
方法执行通过网络缓存降级。
注意:如果回滚失败,将会通过返回null执行“静默失败”的回滚操作。
通过配置 FallbackViaNetwork
命令,使它运行在一个不同的线程池中,而不是来源于HystrixCommandGroupKey
的默认RemoteServiceX
,它将 HystrixThreadPoolKey.Factory.asKey("RemoteServiceXFallback")
注入到构造函数中。
这样子 CommandWithFallbackViaNetwork
将在RemoteServiceX的线程池上运行,而FallbackViaNetwork将运行在 RemoteServiceXFallback
线程池上。
示例如下:
public class HystrixFallbackViaNetwork extends HystrixCommand<String> {
private final int id;
protected HystrixFallbackViaNetwork(int id) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceX"))
.andCommandKey(HystrixCommandKey.Factory.asKey("GetValueCommand")));
this.id = id;
}
@Override
protected String run() {
// 正常情况执行:RemoteServiceXClient.getValue(id);
throw new RuntimeException("force failure for example"); //模拟异常执行降级操作
}
@Override
protected String getFallback() {
return new FallbackViaNetwork(id).execute();
}
private static class FallbackViaNetwork extends HystrixCommand<String> {
private final int id;
public FallbackViaNetwork(int id) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("RemoteServiceX"))
.andCommandKey(HystrixCommandKey.Factory.asKey("GetValueFallbackCommand"))
// 回滚命令使用一个不同的线程池,这样不会被饱和的RemoteServiceX池子阻止执行降级
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("RemoteServiceXFallback")));
this.id = id;
}
@Override
protected String run() {
// MemCacheClient.getValue(id);
// 正常情况应该执行 从缓存(MemCache或者redis)中读取结果
throw new RuntimeException("the fallback also failed"); //模拟异常,执行降级操作
}
@Override
protected String getFallback() {
// 如果降级操作失败则会触发这里进行静态失败降级
return null;
}
}
}
主备降级
很多系统都有双模型--主备或者故障转移。
有时候备机或故障转移可能回事失败的状态,这种情况下,就会像通过网络缓存降级那种模式。然而如果切换到备机或故障转移是常见的。 例如,推出扩展新功能(有时这是有状态系统和处理代码推送的一部分),然后每次使用辅助系统时,主服务器将处于故障状态、跳闸断路器和触发警报。
这不是我们希望的情况,如果没有其他原因的话,那就是避免“狼来了”的疲劳,当真正的问题发生时,它会导致警报被忽略。 所以这种情况,解决策略是将主备的转换视为正常和健康的模式并放一个表象。
(流程示意图)
主服务器和备用服务器的HystrixCommand实现是线程隔离的,因为它们可能正在执行网络流量和业务逻辑。 它们可能都有非常不同的性能特征(通常次要系统是静态缓存),所以隔离的另一个好处是它们可以单独调优。
您不会公开的显示这两个命令,而是将它们隐藏在另一个HystrixCommand后面,该命令是信号隔离的,它实现了是否调用主服务器命令还是备机命令的条件逻辑。如果主命令和备命令都失败了,那么控制切换到本身正面的降级。
正面的HystrixCommand可以使用信号隔离,因为它所做的所有工作都是通过另外两个已经线程隔离的hystrix命令进行的。 只要正面的HystrixCommand中 run()
方法不执行任何其他网络调用、重试逻辑或其他“容易出错”的事情,就没有必要再使用另一层线程了。
public class HystrixFacadeWithPrimarySecondary extends HystrixCommand<String> {
private final static DynamicBooleanProperty usePrimary = DynamicPropertyFactory.getInstance().getBooleanProperty("primarySecondary.usePrimary", true);
private final int id;
public HystrixFacadeWithPrimarySecondary(int id) {
super(Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX"))
.andCommandKey(HystrixCommandKey.Factory.asKey("PrimarySecondaryCommand"))
.andCommandPropertiesDefaults(
// 默认想要的是信号隔离 因为已经包装了两个已经线程隔离的命令
HystrixCommandProperties.Setter()
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)));
this.id = id;
}
@Override
protected String run() {
if (usePrimary.get()) {
return new PrimaryCommand(id).execute();
} else {
return new SecondaryCommand(id).execute();
}
}
@Override
protected String getFallback() {
return "static-fallback-" + id;
}
@Override
protected String getCacheKey() {
return String.valueOf(id);
}
private static class PrimaryCommand extends HystrixCommand<String> {
private final int id;
private PrimaryCommand(int id) {
super(Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX"))
.andCommandKey(HystrixCommandKey.Factory.asKey("PrimaryCommand"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("PrimaryCommand"))
.andCommandPropertiesDefaults(
// 主服务器超时时间默认为600ms
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(600)));
this.id = id;
}
@Override
protected String run() {
// 执行重的“主”服务调用
return "responseFromPrimary-" + id;
}
}
private static class SecondaryCommand extends HystrixCommand<String> {
private final int id;
private SecondaryCommand(int id) {
super(Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("SystemX"))
.andCommandKey(HystrixCommandKey.Factory.asKey("SecondaryCommand"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("SecondaryCommand"))
.andCommandPropertiesDefaults(
// 备服务器超时时间默认为100ms
HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(100)));
this.id = id;
}
@Override
protected String run() {
// 执行快速的备服务调用
return "responseFromSecondary-" + id;
}
}
}
声明
转帖请注明原贴地址:https://my.oschina.net/u/2342969/blog/1817652