如下为博主写Hystrix系列的文章列表,顺便骗个赞,以为写的还能够的,不要吝啬你的赞哟html
点击查看 Hystrix入门java
点击查看 Hystrix命令执行git
点击查看 Hystrix处理异常机制(降级方法)github
点击查看 Hystrix命令名称、分组、线程池redis
点击查看 Hystrix命令名称、Hystrix请求处理后端
点击查看 Hystrix请求处理缓存
点击查看 Hystrix经常使用场景--失败服务器
能够在代码中静态的返回默认值进行降级, 这不会致使功能或服务以“静默失败”的方式被删除,而是致使默认行为发生。cookie
例如,若是一个命令基于用户凭据返回true/false,若是命令执行失败,它能够默认为true:网络
@Override protected Boolean getFallback() { return true; }
HystrixObservableCommand
等价 对于 HystrixObservableCommand
的静默失败解决方案是调用重写 resumeWithFallback
方法,示例以下:
@Override protected Observable<Boolean> resumeWithFallback() { return Observable.just( true ); }
当命令返回的是一个包含多个字段的复合对象时,一般会使用自定义降级,其中一些字段能够由其余请求状态肯定,而其余字段设置为默认值。
适合使用自定义降级的例子有:
降级时会静态的从请求范围检索出存根, 可是若是须要的话,好比下面这个例子演示了它如何处理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