作为一个重试库,首先要解决的问题就是什么时候重试。为了使用guava-retrying,我们需要在pom.xml中加入依赖:
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>19.0</version>
</dependency>
<dependency>
<groupId>com.github.rholder</groupId>
<artifactId>guava-retrying</artifactId>
<version>2.0.0</version>
</dependency>
private static Callable<Void> runtimeExceptionTask = new Callable<Void>() {
@Override
public Void call() throws Exception {
System.out.println("runtime was called.");
throw new NullPointerException("runtime");
}
};
private static Callable<Void> checkedExceptionTask = new Callable<Void>() {
@Override
public Void call() throws Exception {
System.out.println("checked was called.");
throw new IOException("checked");
}
};
private static Callable<Void> errorTask = new Callable<Void>() {
@Override
public Void call() throws Exception {
System.out.println("error was called.");
throw new ThreadDeath();
}
};
Retryer<Void> retryer = RetryerBuilder.<Void>newBuilder()
.retryIfException() // 抛出异常时重试
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后停止
.build();
try {
retryer.call(checkedExceptionTask);
} catch (Exception e) {
}
try {
retryer.call(runtimeExceptionTask);
} catch (Exception e) {
}
try {
retryer.call(errorTask);
} catch (Exception e) {
}
retryIfRuntimeException只会在抛runtime异常的时候才重试,checked异常和error都不重试。
Retryer<Void> retryer = RetryerBuilder.<Void>newBuilder()
.retryIfRuntimeException() // 抛出runtime异常时重试
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后停止
.build();
try {
retryer.call(checkedExceptionTask);
} catch (Exception e) {
}
try {
retryer.call(runtimeExceptionTask);
} catch (Exception e) {
}
try {
retryer.call(errorTask);
} catch (Exception e) {
}
可以看到抛出error是不会进行重试的,当然也没有必要重试。不过通过retryIfExceptionOfType也可以在抛出error的时候进行重试。
Retryer<Void> retryer = RetryerBuilder.<Void>newBuilder()
.retryIfExceptionOfType(Error.class)// 只在抛出error重试
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后停止
.build();
try {
retryer.call(errorTask);
} catch (Exception e) {
}
try {
retryer.call(runtimeExceptionTask);
} catch (Exception e) {
}
try {
retryer.call(checkedExceptionTask);
} catch (Exception e) {
}
retryIfExceptionOfType允许我们只在发生特定异常的时候才重试,比如NullPointerException和IllegalStateException都属于runtime异常。
private static Callable<Void> nullPointerExceptionTask = new Callable<Void>() {
@Override
public Void call() throws Exception {
System.out.println("nullPointerExceptionTask was called.");
throw new NullPointerException();
}
};
private static Callable<Void> illegalStateExceptionTask = new Callable<Void>() {
@Override
public Void call() throws Exception {
System.out.println("illegalStateExceptionTask was called.");
throw new IllegalStateException();
}
};
public static void main(String[] args) {
Retryer<Void> retryer = RetryerBuilder.<Void>newBuilder()
.retryIfExceptionOfType(IllegalStateException.class)
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后停止
.build();
try {
retryer.call(nullPointerExceptionTask);
} catch (Exception e) {
}
try {
retryer.call(illegalStateExceptionTask);
} catch (Exception e) {
}
}
如果我们希望NullPointerException和IllegalStateException发生的时候都重试,其余异常不重试,怎么办呢?有2种实现方式:添加多个retryIfExceptionOfType,通过Predicate。
Retryer<Void> retryer = RetryerBuilder.<Void>newBuilder()
.retryIfExceptionOfType(IllegalStateException.class)
.retryIfExceptionOfType(NullPointerException.class)
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后停止
.build();
try {
retryer.call(nullPointerExceptionTask);
} catch (Exception e) {
}
try {
retryer.call(illegalStateExceptionTask);
} catch (Exception e) {
}
System.out.println("use guava Predicates.");
Retryer<Void> retryer1 = RetryerBuilder.<Void>newBuilder()
.retryIfException(Predicates.or(Predicates.instanceOf(NullPointerException.class),
Predicates.instanceOf(IllegalStateException.class)))
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后停止
.build();
try {
retryer1.call(nullPointerExceptionTask);
} catch (Exception e) {
}
try {
retryer1.call(illegalStateExceptionTask);
} catch (Exception e) {
}
上面我们看到了重试条件都是在发生异常的时候,实际上有时候没有发生异常,但是仍然需要重试的场景也是有的,比如返回false我们希望重试,比如返回字符串符合特定格式我们希望重试,比如返回对象符合某些条件我们希望重试....这个时候我们可以通过retryIfResult实现。通过guava的Predicates,我们可以构建复杂的重试条件。
// 第一次返回false,第二处返回true
private static Callable<Boolean> booleanTask = new Callable<Boolean>() {
private int count = 0;
@Override
public Boolean call() throws Exception {
System.out.println("booleanTask was called.");
if (count == 0) {
count++;
return false;
} else {
return true;
}
}
};
private static Callable<CharSequence> stringTask = new Callable<CharSequence>() {
private int count = 0;
@Override
public CharSequence call() throws Exception {
System.out.println("stringTask was called.");
if (count == 0) {
count++;
return UUID.randomUUID() + "_error";
} else {
return UUID.randomUUID() + "_success";
}
}
};
public static void main(String[] args) {
// 返回false重试
Retryer<Boolean> retryer = RetryerBuilder.<Boolean>newBuilder()
.retryIfResult(Predicates.equalTo(false))
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后停止
.build();
try {
retryer.call(booleanTask);
} catch (Exception e) {
}
System.out.println();
// 以_error结尾才重试
Retryer<CharSequence> retryer1 = RetryerBuilder.<CharSequence>newBuilder()
.retryIfResult(Predicates.containsPattern("_error$"))
.withStopStrategy(StopStrategies.stopAfterAttempt(3)) // 重试3次后停止
.build();
try {
retryer1.call(stringTask);
} catch (Exception e) {
}
}