目录
- 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 ObservableresumeWithFallback() { return Observable.just( true ); }
自定义降级
当命令返回的是一个包含多个字段的复合对象时,通常会使用自定义降级,其中一些字段可以由其他请求状态确定,而其他字段设置为默认值。
适合使用自定义降级的例子有:
- cookies
- 请求参数和请求头
- 之前的服务请求在当前失败之前的响应
降级时会静态的从请求范围检索出存根, 但是如果需要的话,比如下面这个例子演示了它如何处理countryCodeFromGeoLookup字段,通常建议在命令实例化时注入它们。
public class HystrixStubbedFallback extends HystrixCommand{ 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
方法, 用于返回一个可观察者,发送自一定的响应。与前面的例子等价的版本是这样的:
@Overrideprotected ObservableresumeWithFallback() { return Observable.just( new UserAccount(customerId, "Unknown Name", countryCodeFromGeoLookup, true, true, false) );}
但是,如果您期望从 Observable
中发出多个数据项,您可能更感兴趣的是为那些在失败前还没发射原始的 Observable
生成存根。这里有一个简单的例子来说明如何实现这一点——跟踪主Observable
中发出的最后一个数据项,从而使回退知道在何处继续这个顺序:
@Overrideprotected Observableconstruct() { return Observable.just(1, 2, 3) .concatWith(Observable. error(new RuntimeException("forced error"))) .doOnNext(new Action1 () { @Override public void call(Integer t1) { lastSeen = t1; } }) .subscribeOn(Schedulers.computation());}
@Overrideprotected ObservableresumeWithFallback() { if (lastSeen < 4) { return Observable.range(lastSeen + 1, 4 - lastSeen); } else { return Observable.empty(); }}
通过网络缓存降级
有时如果后端服务失败,可以从缓存服务(如memcached)检索过时的数据版本。 由于回退会超出网络,所以它是另一个可能的失败点,因此它也需要被一个HystrixCommand或HystrixObservableCommand包裹。
(执行示意图)
在单独的线程池上执行降级(回退)命令是很重要的,否则,如果主命令被隐藏并填充线程池,因为两个命令共享同一个池,将阻止降级(回退)。
下面将举例怎样使用 CommandWithFallbackViaNetwork
中的 getFallback()
方法执行通过网络缓存降级。
注意:如果回滚失败,将会通过返回null执行“静默失败”的回滚操作。
通过配置 FallbackViaNetwork
命令,使它运行在一个不同的线程池中,而不是来源于的默认RemoteServiceX
,它将 HystrixThreadPoolKey.Factory.asKey("RemoteServiceXFallback")
注入到构造函数中。
这样子 CommandWithFallbackViaNetwork
将在RemoteServiceX的线程池上运行,而FallbackViaNetwork将运行在 RemoteServiceXFallback
线程池上。
示例如下:
public class HystrixFallbackViaNetwork extends HystrixCommand{ 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 { 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中 方法不执行任何其他网络调用、重试逻辑或其他“容易出错”的事情,就没有必要再使用另一层线程了。
public class HystrixFacadeWithPrimarySecondary extends HystrixCommand{ 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 { 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 { 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