博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
教程:一起学习Hystrix--Hystrix常用场景--降级(回退)
阅读量:5737 次
发布时间:2019-06-18

本文共 10068 字,大约阅读时间需要 33 分钟。

hot3.png

目录

  • 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
resumeWithFallback() { 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 Observable
resumeWithFallback() { return Observable.just( new UserAccount(customerId, "Unknown Name", countryCodeFromGeoLookup, true, true, false) );}

     但是,如果您期望从 Observable 中发出多个数据项,您可能更感兴趣的是为那些在失败前还没发射原始的 Observable 生成存根。这里有一个简单的例子来说明如何实现这一点——跟踪主Observable 中发出的最后一个数据项,从而使回退知道在何处继续这个顺序:

@Overrideprotected Observable
construct() { 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 Observable
resumeWithFallback() { if (lastSeen < 4) { return Observable.range(lastSeen + 1, 4 - lastSeen); } else { return Observable.empty(); }}

通过网络缓存降级

     有时如果后端服务失败,可以从缓存服务(如memcached)检索过时的数据版本。 由于回退会超出网络,所以它是另一个可能的失败点,因此它也需要被一个HystrixCommand或HystrixObservableCommand包裹。

214609_z8kK_2342969.png(执行示意图)

    在单独的线程池上执行降级(回退)命令是很重要的,否则,如果主命令被隐藏并填充线程池,因为两个命令共享同一个池,将阻止降级(回退)。

    下面将举例怎样使用 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; } }}

主备降级

    很多系统都有双模型--主备或者故障转移。

    有时候备机或故障转移可能回事失败的状态,这种情况下,就会像通过网络缓存降级那种模式。然而如果切换到备机或故障转移是常见的。 例如,推出扩展新功能(有时这是有状态系统和处理代码推送的一部分),然后每次使用辅助系统时,主服务器将处于故障状态、跳闸断路器和触发警报。

    这不是我们希望的情况,如果没有其他原因的话,那就是避免“狼来了”的疲劳,当真正的问题发生时,它会导致警报被忽略。 所以这种情况,解决策略是将主备的转换视为正常和健康的模式并放一个表象。

113332_0Ns8_2342969.png(流程示意图)

    主服务器和备用服务器的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

转载于:https://my.oschina.net/u/2342969/blog/1818021

你可能感兴趣的文章
bzoj 2282: [Sdoi2011]消防
查看>>
我的友情链接
查看>>
centos5.9使用RPM包搭建lamp平台
查看>>
关于C#面向对象2
查看>>
Javascript String类的属性及方法
查看>>
vim编辑器如何添加或删除多行注释
查看>>
[LeetCode] Merge Intervals
查看>>
iOS开发-按钮的基本使用
查看>>
在QT和SDL搭建的框架中使用OPENGL在SDL窗口上进行绘图
查看>>
REST技术第三步 @BeanParam的使用
查看>>
SharePoint 读取 Site Columns 的数据并绑定到DropdownList
查看>>
Python中的对象行为与特殊方法(二)类型检查与抽象基类
查看>>
使用 axios 详解
查看>>
通信基站(dfs回溯,思维)
查看>>
nginx web加密访问
查看>>
iOS - Regex 正则表达式
查看>>
第 68 章 Logical Volume Manager (LVM)
查看>>
膝盖中了一箭之康复篇-第八个月暨2月份目标总结
查看>>
IPA提交APPStore问题记录(一)
查看>>
有利于seo优化的网站地图不能取巧
查看>>