以下の解決策があります。
スローする代わりに例外を返す
最も単純で汚いアプローチ。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 によってスローされる他のすべての例外とは異なり、これはフォールバックをトリガーせず、障害メトリックに対してカウントされないため、サーキット ブレーカーをトリガーしません。
のインスタンスは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);
})