com.github.phantomthief.failover.util.FailoverUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of simple-failover Show documentation
Show all versions of simple-failover Show documentation
A simple failover library for Java
package com.github.phantomthief.failover.util;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Predicates.alwaysTrue;
import static com.google.common.base.Throwables.getRootCause;
import static com.google.common.util.concurrent.Uninterruptibles.sleepUninterruptibly;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import java.net.ConnectException;
import java.net.MalformedURLException;
import java.net.NoRouteToHostException;
import java.net.SocketTimeoutException;
import java.net.UnknownHostException;
import java.util.HashSet;
import java.util.Set;
import java.util.function.Predicate;
import javax.annotation.Nonnegative;
import javax.annotation.Nonnull;
import com.github.phantomthief.failover.Failover;
import com.github.phantomthief.failover.exception.NoAvailableResourceException;
import com.github.phantomthief.util.ThrowableConsumer;
import com.github.phantomthief.util.ThrowableFunction;
/**
* @author w.vela
*/
public class FailoverUtils {
private FailoverUtils() {
throw new UnsupportedOperationException();
}
public static R supplyWithRetry(int maxRetryTimes,
long sleepBetweenRetryMs, Failover failover, ThrowableFunction func)
throws X {
return supplyWithRetry(maxRetryTimes, sleepBetweenRetryMs, failover, func, alwaysTrue());
}
/**
* @param failChecker {@code true} if need retry, {@code false} means no need retry and mark success
*/
public static R supplyWithRetry(@Nonnegative int maxRetryTimes,
long sleepBetweenRetryMs, Failover failover, ThrowableFunction func,
@Nonnull Predicate failChecker) throws X {
checkArgument(maxRetryTimes > 0);
Set failed = new HashSet<>();
Throwable lastError = null;
for (int i = 0; i < maxRetryTimes; i++) {
T oneAvailable = failover.getOneAvailableExclude(failed);
if (oneAvailable != null) {
try {
R result = func.apply(oneAvailable);
failover.success(oneAvailable);
return result;
} catch (Throwable e) {
if (failChecker.test(e)) {
failover.fail(oneAvailable);
failed.add(oneAvailable);
if (sleepBetweenRetryMs > 0) {
sleepUninterruptibly(sleepBetweenRetryMs, MILLISECONDS);
}
lastError = e;
continue;
} else {
failover.success(oneAvailable);
throw e;
}
}
} else {
throw new NoAvailableResourceException();
}
}
//noinspection unchecked
throw (X) lastError;
}
public static R supply(Failover failover,
ThrowableFunction func, Predicate failChecker) throws X {
T oneAvailable = failover.getOneAvailable();
if (oneAvailable != null) {
try {
R result = func.apply(oneAvailable);
failover.success(oneAvailable);
return result;
} catch (Throwable e) {
if (failChecker == null || failChecker.test(e)) {
failover.fail(oneAvailable);
} else {
failover.success(oneAvailable);
}
throw e;
}
} else {
throw new NoAvailableResourceException();
}
}
public static void runWithRetry(int maxRetryTimes,
long sleepBetweenRetryMs, Failover failover, ThrowableConsumer func) throws X {
supplyWithRetry(maxRetryTimes, sleepBetweenRetryMs, failover, t -> {
func.accept(t);
return null;
}, alwaysTrue());
}
/**
* @param failChecker {@code true} if need retry, {@code false} means no need retry and mark success
*/
public static void runWithRetry(int maxRetryTimes,
long sleepBetweenRetryMs, Failover failover, ThrowableConsumer func,
@Nonnull Predicate failChecker) throws X {
supplyWithRetry(maxRetryTimes, sleepBetweenRetryMs, failover, t -> {
func.accept(t);
return null;
}, failChecker);
}
public static void run(Failover failover,
ThrowableConsumer func, Predicate failChecker) throws X {
supply(failover, t -> {
func.accept(t);
return null;
}, failChecker);
}
public static boolean isHostUnavailable(Throwable t) {
Throwable rootCause = getRootCause(t);
if (rootCause instanceof NoRouteToHostException) {
return true;
}
if (rootCause instanceof UnknownHostException) {
return false;
}
if (rootCause instanceof MalformedURLException) {
return false;
}
if (rootCause instanceof ConnectException) {
if (rootCause.getMessage() != null
&& rootCause.getMessage().toLowerCase().contains("connection refused")) {
return true;
}
}
if (rootCause instanceof SocketTimeoutException) {
if (rootCause.getMessage() != null
&& rootCause.getMessage().toLowerCase().contains("connect timed out")) {
return true;
}
}
return false;
}
}