ドライバアプリケーション:
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
new Application().doSomething();
}
public void doSomething() {
System.out.println("Doing something");
}
}
スロットル例外クラス:
package de.scrum_master.app;
public class ThrottlingException extends RuntimeException {
private static final long serialVersionUID = 1L;
public ThrottlingException(String arg0) {
super(arg0);
}
}
スロットリングインターセプタ:私はヘルパーメソッドを作成し、絞り状況をエミュレートするために
isThrottled()
は、3例中2例でランダムにtrue
を返します。
package de.scrum_master.aspect;
import java.util.Random;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import de.scrum_master.app.ThrottlingException;
@Aspect
public class ThrottlingInterceptor {
private static final Random RANDOM = new Random();
@Before("execution(* doSomething())")
public void invoke(JoinPoint thisJoinPoint) throws ThrottlingException {
System.out.println(getClass().getSimpleName() + " -> " + thisJoinPoint);
if (isThrottled()) {
throw new ThrottlingException("call throttled");
}
}
private boolean isThrottled() {
return RANDOM.nextInt(3) > 0;
}
}
再試行インターセプタ:
AspectJの注釈@DeclarePrecedence("RetryInterceptor, *")
はこのインターセプタは、他のものの前に実行されることを述べていることに注意してください。両方のインターセプタクラスでアノテーションを@Order
アノテーションに置き換えてください。さもなければ、@Around
アドバイスは、スロットルインターセプターによってスローされた例外をキャッチできません。
また、このインターセプタはリトライロジックを実装するためにリフレクションを必要としないこと、リトライループ内でjoinpointを直接使用してthisJoinPoint.proceed()
を再試行することにも言及する価値があります。これは、さまざまな種類の再試行動作を実装するヘルパー・メソッドまたはヘルパー・クラスに簡単に組み込むことができます。 Callable
ではなく、ProceedingJoinPoint
をパラメータとして使用してください。
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclarePrecedence;
import de.scrum_master.app.ThrottlingException;
@Aspect
@DeclarePrecedence("RetryInterceptor, *")
public class RetryInterceptor {
private static int MAX_TRIES = 5;
private static int WAIT_MILLIS_BETWEEN_TRIES = 1000;
@Around("execution(* doSomething())")
public Object invoke(ProceedingJoinPoint thisJoinPoint) throws Throwable {
System.out.println(getClass().getSimpleName() + " -> " + thisJoinPoint);
ThrottlingException throttlingException = null;
for (int i = 1; i <= MAX_TRIES; i++) {
try {
return thisJoinPoint.proceed();
}
catch (ThrottlingException e) {
throttlingException = e;
System.out.println(" Throttled during try #" + i);
Thread.sleep(WAIT_MILLIS_BETWEEN_TRIES);
}
}
throw throttlingException;
}
}
リトライ成功のためのコンソールログ:失敗した再試行のための
RetryInterceptor -> execution(void de.scrum_master.app.Application.doSomething())
ThrottlingInterceptor -> execution(void de.scrum_master.app.Application.doSomething())
Throttled during try #1
ThrottlingInterceptor -> execution(void de.scrum_master.app.Application.doSomething())
Throttled during try #2
ThrottlingInterceptor -> execution(void de.scrum_master.app.Application.doSomething())
Doing something
コンソールログ:
RetryInterceptor -> execution(void de.scrum_master.app.Application.doSomething())
ThrottlingInterceptor -> execution(void de.scrum_master.app.Application.doSomething())
Throttled during try #1
ThrottlingInterceptor -> execution(void de.scrum_master.app.Application.doSomething())
Throttled during try #2
ThrottlingInterceptor -> execution(void de.scrum_master.app.Application.doSomething())
Throttled during try #3
ThrottlingInterceptor -> execution(void de.scrum_master.app.Application.doSomething())
Throttled during try #4
ThrottlingInterceptor -> execution(void de.scrum_master.app.Application.doSomething())
Throttled during try #5
Exception in thread "main" de.scrum_master.app.ThrottlingException: call throttled
at de.scrum_master.aspect.ThrottlingInterceptor.invoke(ThrottlingInterceptor.aj:19)
at de.scrum_master.app.Application.doSomething_aroundBody0(Application.java:9)
at de.scrum_master.app.Application.doSomething_aroundBody1$advice(Application.java:22)
at de.scrum_master.app.Application.doSomething(Application.java:1)
at de.scrum_master.app.Application.main(Application.java:5)
は私の答えに関連するフォローアップの質問をお気軽に。
アップデート:私はあなたのRetryingCallable
とRetryPolicy
のクラス/インタフェースがどのように機能するか見当がつかない、あなたは私にそれについて多くを教えてくれませんでした。しかし、私は何かを作り、それは次のように働いてしまった:ログ出力はかなり似たものになります
package de.scrum_master.aspect;
import java.util.concurrent.Callable;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.DeclarePrecedence;
import de.scrum_master.app.DefaultRetryPolicy;
import de.scrum_master.app.RetryPolicy;
import de.scrum_master.app.RetryingCallable;
@Aspect
@DeclarePrecedence("RetryInterceptor, *")
public class RetryInterceptor {
private RetryPolicy<Object> retryPolicy = new DefaultRetryPolicy<>();
@Around("execution(* doSomething())")
public Object invoke(ProceedingJoinPoint thisJoinPoint) throws Throwable {
System.out.println(getClass().getSimpleName() + " -> " + thisJoinPoint);
return RetryingCallable.newRetryingCallable(
new Callable<Object>() {
@Override
public Object call() throws Exception {
return thisJoinPoint.proceed();
}
},
retryPolicy
).call();
}
}
:
package de.scrum_master.app;
import java.util.concurrent.Callable;
public interface RetryPolicy<V> {
V apply(Callable<V> callable) throws Exception;
}
package de.scrum_master.app;
import java.util.concurrent.Callable;
public class DefaultRetryPolicy<V> implements RetryPolicy<V> {
private static int MAX_TRIES = 5;
private static int WAIT_MILLIS_BETWEEN_TRIES = 1000;
@Override
public V apply(Callable<V> callable) throws Exception {
Exception throttlingException = null;
for (int i = 1; i <= MAX_TRIES; i++) {
try {
return callable.call();
}
catch (ThrottlingException e) {
throttlingException = e;
System.out.println(" Throttled during try #" + i);
Thread.sleep(WAIT_MILLIS_BETWEEN_TRIES);
}
}
throw throttlingException;
}
}
package de.scrum_master.app;
import java.util.concurrent.Callable;
public class RetryingCallable<V> {
private RetryPolicy<V> retryPolicy;
private Callable<V> callable;
public RetryingCallable(Callable<V> callable, RetryPolicy<V> retryPolicy) {
this.callable = callable;
this.retryPolicy = retryPolicy;
}
public static <V> RetryingCallable<V> newRetryingCallable(Callable<V> callable, RetryPolicy<V> retryPolicy) {
return new RetryingCallable<V>(callable, retryPolicy);
}
public V call() throws Exception {
return retryPolicy.apply(callable);
}
}
さて、このような再試行インターセプタを変更しますあなたは前に見た。私にとってこれはうまく動作します。
お返事ありがとうございます。私はすでに '@ Ordered'を使用しています。私は '@ ThrowsException'アドバイスを使用しているので、ここでは1つの質問です。私は無限ループに走っています。しかし、あなたのコードは '@Around'で正常に動作しているようですが、私がここで欠けている微妙な違いは何ですか? – AgentX
おそらく '@ AfterThrowing'を意味します。名前の通り、そのアドバイス型は、メソッドの実行を傍受します。@終了した後、 '@Around'自体がラップします。つまり、前後に何かできること、例外をキャッチして処理したり、メソッドの実行を再試行したりします。 '@ AfterThrowing' isはすでに遅すぎます。例外がスローされ、メソッドの実行が終了しました。さらに、私のログの例外コールスタックをチェックしてください。例外は 'ThrottlingInterceptor.invoke'にスローされるので、間違った' @AfterThrowing'のポイントカットがあったかもしれません。 – kriegaex
ありがとう、私はループを追加し、あなたが説明したようにこれをやり直しましたが、これは今のところうまくいきます。一つの最後のことです:将来私は上記の例で使っていたように( '3 retries'、' randomness'と '指数バックオフ'のような良いデフォルトを提供する)一般的な再試行ポリシーを使いたいと思います。 'return RetryingCallable .NewRetryingCallable(new Callable