5

Hystrix がコマンドから発生するすべての例外をサーキット ブレーカーの目的でエラーとして処理することを確認しました。これには、コマンド run () メソッドからスローされ、Hystrix 自体によって作成される例外 (HystrixTimeoutException など) が含まれます。

しかし、さらに処理する必要がある有効なエラーでサービスが応答したことを示す run() メソッドからスローされるビジネス例外があります。そのような例外の 1 つの例は、SpringWS から WebServiceTemplate を使用している間の WebServiceFaultException です。

したがって、回路をトリップするためにこれらの特定の例外は必要ありません。この動作はどのように達成できますか?

ビジネス例外をホルダー オブジェクトにラップし、それを run() メソッドから返し、ラップを解除して Exception に戻し、再スローする明白な方法があります。しかし、よりクリーンな方法があるかどうか疑問に思っていました。

4

1 に答える 1

3

以下の解決策があります。

スローする代わりに例外を返す

最も単純で汚いアプローチ。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);
        }
    }
});

ホルダー オブジェクトを使用して結果と例外の両方を保持する

このアプローチでは、追加のホルダー クラスを導入する必要があります (これは、他の目的で単独で使用することもできます)。

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 で最適です。

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

HystrixBadRequestException を使用する

HystrixBadRequestExceptionは特別な種類の例外であり、サーキット ブレーカーとメトリックに関しては失敗としてカウントされません。ドキュメントに見られるように:

HystrixCommand によってスローされる他のすべての例外とは異なり、これはフォールバックをトリガーせず、障害メトリックに対してカウントされないため、サーキット ブレーカーをトリガーしません。

のインスタンスはHystrixBadRequestExceptionHystrix 自体によって作成されないため、ビジネス例外のラッパーとして安全に使用できます。ただし、元のビジネス例外はラップ解除する必要があります。

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);
})
于 2016-07-07T22:18:10.520 に答える