
com.github.phantomthief.failover.impl.WeightFailover Maven / Gradle / Ivy
package com.github.phantomthief.failover.impl;
import static com.github.phantomthief.tuple.Tuple.tuple;
import static com.github.phantomthief.util.MoreSuppliers.lazy;
import static com.google.common.primitives.Ints.constrainToRange;
import static java.lang.Integer.MAX_VALUE;
import static java.lang.Math.max;
import static java.lang.Math.min;
import static java.util.Collections.emptySet;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.stream.Collectors.toSet;
import static org.slf4j.LoggerFactory.getLogger;
import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.function.Consumer;
import java.util.function.IntUnaryOperator;
import org.slf4j.Logger;
import com.github.phantomthief.failover.Failover;
import com.github.phantomthief.failover.util.SharedCheckExecutorHolder;
import com.github.phantomthief.tuple.TwoTuple;
import com.github.phantomthief.util.MoreSuppliers.CloseableSupplier;
/**
* 默认权重记录
* fail时权重下降
* success时权重恢复
*
* @author w.vela
*/
public class WeightFailover implements Failover, Closeable {
private static final Logger logger = getLogger(WeightFailover.class);
private final IntUnaryOperator failReduceWeight;
private final IntUnaryOperator successIncreaseWeight;
private final ConcurrentMap initWeightMap;
private final ConcurrentMap currentWeightMap;
private final CloseableSupplier> recoveryFuture;
private final Consumer onMinWeight;
private final int minWeight;
/**
* {@code null} if this feature is off.
*/
private final Integer weightOnMissingNode;
private volatile boolean closed;
WeightFailover(WeightFailoverBuilder builder) {
this.minWeight = builder.minWeight;
this.failReduceWeight = builder.failReduceWeight;
this.successIncreaseWeight = builder.successIncreaseWeight;
this.initWeightMap = new ConcurrentHashMap<>(builder.initWeightMap);
this.currentWeightMap = new ConcurrentHashMap<>(builder.initWeightMap);
this.onMinWeight = builder.onMinWeight;
this.weightOnMissingNode = builder.weightOnMissingNode;
this.recoveryFuture = lazy(
() -> SharedCheckExecutorHolder.getInstance().scheduleWithFixedDelay(() -> {
if (closed) {
tryCloseRecoveryScheduler();
return;
}
Thread currentThread = Thread.currentThread();
String origName = currentThread.getName();
if (builder.name != null) {
currentThread.setName(origName + "-[" + builder.name + "]");
}
try {
Map recoveredObjects = new HashMap<>();
this.currentWeightMap.forEach((obj, weight) -> {
if (weight == 0) {
double recoverRate = builder.checker.applyAsDouble(obj);
if (recoverRate > 0) {
recoveredObjects.put(obj, recoverRate);
}
}
});
if (!recoveredObjects.isEmpty()) {
logger.info("found recovered objects:{}", recoveredObjects);
}
recoveredObjects.forEach((recovered, rate) -> {
Integer initWeight = initWeightMap.get(recovered);
if (initWeight == null) {
throw new IllegalStateException("obj:" + recovered);
}
int recoveredWeight = constrainToRange((int) (initWeight * rate), 1,
initWeight);
currentWeightMap.put(recovered, recoveredWeight);
if (builder.onRecovered != null) {
builder.onRecovered.accept(recovered);
}
});
} catch (Throwable e) {
logger.error("", e);
} finally {
currentThread.setName(origName);
}
}, builder.checkDuration, builder.checkDuration, MILLISECONDS));
}
/**
* better use {@link #newGenericBuilder()} for type safe
*/
@Deprecated
public static WeightFailoverBuilder
© 2015 - 2025 Weber Informatics LLC | Privacy Policy