教程:一块儿学习Hystrix--Hystrix经常使用场景--降级(回退)

目录

  • Hystrix本系列博文
  • 静态降级(返回默认值)
  • 自定义降级
  • 网络缓存降级
  • 主备降级
  • 声明

Hystrix本系列博文

    如下为博主写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 );
    }

自定义降级

     当命令返回的是一个包含多个字段的复合对象时,一般会使用自定义降级,其中一些字段能够由其余请求状态肯定,而其余字段设置为默认值。

     适合使用自定义降级的例子有:

  • 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

相关文章
相关标签/搜索