【问题标题】:Hystrix circuit breaker with business exceptions具有业务异常的 Hystrix 断路器
【发布时间】:2016-03-15 05:05:22
【问题描述】:

我观察到 Hystrix 将所有来自命令的异常都视为故障,以达到断路的目的。它包括从命令 run() 方法抛出并由 Hystrix 本身创建的异常,例如HystrixTimeoutException。

但是我有业务异常要从 run() 方法抛出,这表示该服务以必须进一步处理的有效错误响应。 此类异常的一个示例是使用 SpringWS 中的 WebServiceTemplate 时出现 WebServiceFaultException。

所以我不需要那些特定的例外来使电路跳闸。 如何实现这种行为?

有一种显而易见的方法可以将业务异常包装到持有者对象中,从 run() 方法返回它,然后将其解包回 Exception 并重新抛出。但它想知道是否有更清洁的方法。

【问题讨论】:

  • 这是个老话题,但我的问题是:为什么在 Hystrix 上下文(基础设施)中有业务异常?你错过了一些翻译。业务异常的技术异常应该在hystrix之上进行改造。

标签: java hystrix circuit-breaker


【解决方案1】:

有以下解决方案。

返回异常而不是抛出

最直接和肮脏的方法。这看起来有点古怪,因为您必须将命令删除到 Object 并且有很多类型转换。

Observable<BusinessResponse> observable = new HystrixCommand<Object>() {
    @Override
    protected Object run() throws Exception {
        try {
            return doStuff(...);
        } catch (BusinessException e) {
            return e; // so Hystrix won't treat it as a failure
        }
    }
})
.observe()
.flatMap(new Func1<Object, Observable<BusinessResponse>>() {
    @Override
    public Observable<BusinessResponse> call(Object o) {
        if (o instanceof BusinessException) {
            return Observable.error((BusinessException)o);
        } else {
            return Observable.just((BusinessResponse)o);
        }
    }
});

使用 holder 对象来保存结果和异常

此方法需要引入额外的持有者类(也可以单独用于其他目的)。

class ResultHolder<T, E extends Exception> {
    private T result;
    private E exception;

    public ResultHolder(T result) {
        this.result = result;
    }
    public ResultHolder(E exception) {
        if (exception == null) {
            throw new IllegalArgumentException("exception can not be null");
        }
        this.exception = exception;
    }

    public T get() throws E {
        if (exception != null) {
            throw exception;
        } else {
            return result;
        }
    }

    public Observable<T> observe() {
        if (exception != null) {
            return Observable.error(exception);
        } else {
            return Observable.just(result);
        }
    }

    @SuppressWarnings("unchecked")
    public static <T, E extends Exception> ResultHolder<T, E> wrap(BusinessMethod<T, E> method) {
        try {
            return new ResultHolder<>(method.call());
        } catch (Exception e) {
            return new ResultHolder<>((E)e);
        }
    }


    public static <T, E extends Exception> Observable<T> unwrap(ResultHolder<T, E> holder) {
        return holder.observe();
    }

    interface BusinessMethod<T, E extends Exception> {
        T call() throws E;
    }
}

现在使用它的代码看起来更干净了,唯一的缺点可能是泛型数量很多。此外,这种方法在 Java 8 中是最好的,其中 lambdas 和方法引用可用,否则它看起来很笨重。

new HystrixCommand<ResultHolder<BusinessResponse, BusinessException>>() {
    @Override
    protected ResultHolder<BusinessResponse, BusinessException> run() throws Exception {
        return ResultHolder.wrap(() -> doStuff(...));
    }
}
.observe()
.flatMap(ResultHolder::unwrap);

使用 HystrixBadRequestException

HystrixBadRequestException 是一种特殊的异常,在断路器和指标方面不会被视为失败。如documentation

与 HystrixCommand 抛出的所有其他异常不同,这不会 触发回退,不计入故障指标,因此不 触发断路器。

HystrixBadRequestException 的实例不是由 Hystrix 自己创建的,因此可以安全地将其用作业务异常的包装器。但是,原始业务异常仍然需要解包。

new HystrixCommand<BusinessResponse>() {
    @Override
    protected BusinessResponse run() throws Exception {
        try {
            return doStuff(...);
        } catch (BusinessException e) {
            throw new HystrixBadRequestException("Business exception occurred", e);
        }
    }
}
.observe()
.onErrorResumeNext(e -> {
    if (e instanceof HystrixBadRequestException) {
        e = e.getCause(); // Unwrap original BusinessException
    }
    return Observable.error(e);
})

【讨论】:

    猜你喜欢
    • 2018-07-09
    • 2020-10-29
    • 2015-06-19
    • 2016-01-11
    • 2016-12-11
    • 2016-08-06
    • 2018-10-05
    • 2019-06-27
    • 2015-12-06
    相关资源
    最近更新 更多