com.github.phantomthief.failover.impl.PriorityFailoverBuilder 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.impl;
import static java.util.Objects.requireNonNull;
import java.time.Duration;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ScheduledExecutorService;
import java.util.function.Predicate;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import com.github.phantomthief.failover.util.SharedCheckExecutorHolder;
/**
* PriorityFailover的builder。
*
* @author huangli
* Created on 2020-01-16
*/
public class PriorityFailoverBuilder {
private static final double DEFAULT_MAX_WEIGHT = 100.0;
private static final double DEFAULT_MIN_WEIGHT = 0.0;
private static final int DEFAULT_PRIORITY = 0;
private PriorityFailoverConfig config = new PriorityFailoverConfig<>();
private int[] coreGroupSizes;
PriorityFailoverBuilder() {
}
/**
* 构造一个PriorityFailover,PriorityFailover构造后是不能添加和删除资源的,如果有这方面的需求应该构建一个
* PriorityFailoverManager。
* @see #buildManager()
* @return 一个新的PriorityFailover
*/
public PriorityFailover build() {
PriorityFailoverConfig configCopy = config.clone();
int[] coreGroupSizesCopy = coreGroupSizes == null ? null : coreGroupSizes.clone();
buildGroup(configCopy, coreGroupSizesCopy);
return new PriorityFailover<>(configCopy);
}
/**
* 构造一个PriorityFailoverManager,可以管理资源的添加和删除,如果资源列表是固定的,
* 可用build方法直接构建PriorityFailover。
* @see #build()
* @return 一个PriorityFailoverManager
*/
public PriorityFailoverManager buildManager() {
PriorityFailoverConfig configCopy = config.clone();
int[] coreGroupSizesCopy = coreGroupSizes == null ? null : coreGroupSizes.clone();
PriorityGroupManager groupManager = buildGroup(configCopy, coreGroupSizesCopy);
PriorityFailover priorityFailover = new PriorityFailover<>(configCopy);
return new PriorityFailoverManager<>(priorityFailover, groupManager);
}
private static PriorityGroupManager buildGroup(PriorityFailoverConfig config, int[] coreGroupSizes) {
if (coreGroupSizes != null && coreGroupSizes.length > 0) {
Map resources = config.getResources();
PriorityGroupManager groupManager = new PriorityGroupManager<>(
resources.keySet(), coreGroupSizes);
Map priorityMap = groupManager.getPriorityMap();
priorityMap.forEach((res, pri) -> {
ResConfig old = resources.get(res);
ResConfig newConfig = new ResConfig(old.getMaxWeight(), old.getMinWeight(), pri, old.getInitWeight());
resources.put(res, newConfig);
});
return groupManager;
} else {
return null;
}
}
/**
* 添加资源,使用默认的配置,最大权重100.0,最小权重0,优先级0,初始权重100.0。
* @param res 要添加的资源
* @return this
*/
public PriorityFailoverBuilder addResource(T res) {
return addResource(res, DEFAULT_MAX_WEIGHT);
}
/**
* 添加资源,最小权重0,优先级0,初始权重等于最大权重。
* @param res 要添加的资源
* @param maxWeight 最大权重
* @return this
*/
public PriorityFailoverBuilder addResource(T res, double maxWeight) {
return addResource(res, maxWeight, DEFAULT_MIN_WEIGHT);
}
/**
* 添加资源,优先级0,初始权重等于最大权重。
* @param res 要添加的资源
* @param maxWeight 最大权重
* @param minWeight 最小权重
* @return this
*/
public PriorityFailoverBuilder addResource(T res, double maxWeight, double minWeight) {
return addResource(res, maxWeight, minWeight, DEFAULT_PRIORITY);
}
/**
* 添加资源,初始权重等于最大权重。
* @param res 要添加的资源
* @param maxWeight 最大权重
* @param minWeight 最小权重
* @param priority 优先级
* @return this
*/
public PriorityFailoverBuilder addResource(T res, double maxWeight, double minWeight, int priority) {
return addResource(res, maxWeight, minWeight, priority, maxWeight);
}
/**
* 添加资源。
* @param res 要添加的资源
* @param maxWeight 最大权重
* @param minWeight 最小权重
* @param priority 优先级
* @param initWeight 初始权重
* @return this
*/
public PriorityFailoverBuilder addResource(T res, double maxWeight, double minWeight,
int priority, double initWeight) {
requireNonNull(res);
ResConfig resConfig = new ResConfig(maxWeight, minWeight, priority, initWeight);
checkResConfig(resConfig);
config.getResources().put(res, resConfig);
return this;
}
/**
* 一次添加多个资源,使用默认的配置,最大权重100.0,最小权重0,优先级0,初始权重100.0。
* @param resources 资源列表
* @return this
*/
public PriorityFailoverBuilder addResources(@Nonnull Collection extends T> resources) {
addResources(resources, DEFAULT_MAX_WEIGHT);
return this;
}
/**
* 一次添加多个资源,除了指定了最大权重外,其他使用默认的配置,最小权重0,优先级0,初始权重等于最大权重。
* @param resources 资源列表
* @param maxWeight 所有资源的最大权重
* @return this
*/
public PriorityFailoverBuilder addResources(@Nonnull Collection extends T> resources, double maxWeight) {
Objects.requireNonNull(resources);
resources.forEach(res -> addResource(res, maxWeight));
return this;
}
/**
* 一次添加多个资源,除了指定了最大权重外,其他使用默认的配置,最小权重0,优先级0,初始权重等于最大权重。
* @param resources 资源列表,map中的value就是对应资源的最大权重
* @return this
*/
public PriorityFailoverBuilder addResources(@Nonnull Map extends T, ? extends Number> resources) {
Objects.requireNonNull(resources);
resources.forEach((res, maxWeight) -> addResource(res, maxWeight.doubleValue()));
return this;
}
static void checkResConfig(ResConfig resConfig) {
if (resConfig.getMaxWeight() < 0) {
throw new IllegalArgumentException("maxWeight less than zero:" + resConfig.getMaxWeight());
}
if (resConfig.getMinWeight() < 0) {
throw new IllegalArgumentException("minWeight less than zero:" + resConfig.getMinWeight());
}
if (resConfig.getMaxWeight() < resConfig.getMinWeight()) {
throw new IllegalArgumentException(
"maxWeight < minWeight:" + resConfig.getMaxWeight() + "," + resConfig.getMinWeight());
}
if (resConfig.getInitWeight() < resConfig.getMinWeight() && resConfig.getInitWeight() > resConfig
.getMaxWeight()) {
throw new IllegalArgumentException("illegal initWeight:" + resConfig.getInitWeight());
}
}
/**
* 指定failover的name。
* @param name 这个failover的name
* @return this
*/
public PriorityFailoverBuilder name(String name) {
config.setName(name);
return this;
}
/**
* 指定权重计算的回调,如果不需要定制可以不指定,用默认的({@link RatioWeightFunction})就可以。
* @param weightFunction 权重回调
* @return this
* @see WeightFunction
*/
public PriorityFailoverBuilder weightFunction(WeightFunction weightFunction) {
requireNonNull(weightFunction);
config.setWeightFunction(weightFunction);
return this;
}
/**
* 注册权重事件回调。
* @param weightListener 回调器
* @return this
* @see WeightListener
*/
public PriorityFailoverBuilder weightListener(WeightListener weightListener) {
requireNonNull(weightListener);
config.setWeightListener(weightListener);
return this;
}
/**
* 优先级因子,默认1.4与envoy相同。
* 可参考envoy负载均衡文档
* @param priorityFactor 优先级因子
* @return
*/
public PriorityFailoverBuilder priorityFactor(double priorityFactor) {
if (priorityFactor < 0) {
throw new IllegalArgumentException("priorityFactor less than zero:" + priorityFactor);
}
config.setPriorityFactor(priorityFactor);
return this;
}
/**
* 激活自动优先级管理,假设现在有N个资源,想分3组,第一组5个优先级0,第二组20个优先级1,剩下的归为第三组优先级2,那么调用本方法
* enableAutoPriority(5, 20)即可
* @param coreGroupSizes 每组的资源数字,最后一组不用填
* @return this
*/
@SuppressWarnings("checkstyle:HiddenField")
public PriorityFailoverBuilder enableAutoPriority(int... coreGroupSizes) {
this.coreGroupSizes = coreGroupSizes;
return this;
}
/**
* 设定健康检查时间间隔,注意后台健康检查器是懒启动的,只有在有资源出现访问失败以后才会启动。
* @param checkDuration 健康检查时间间隔
* @return this
* @see #startCheckTaskImmediately(boolean)
*/
public PriorityFailoverBuilder checkDuration(Duration checkDuration) {
requireNonNull(checkDuration);
config.setCheckDuration(checkDuration);
return this;
}
/**
* 指定健康检查使用的线程池,如果不指定会用默认的。
* @param checkExecutor 线程池
* @return this
*/
public PriorityFailoverBuilder checkExecutor(ScheduledExecutorService checkExecutor) {
requireNonNull(checkExecutor);
config.setCheckExecutor(checkExecutor);
return this;
}
/**
* 注册健康检查器回调,检查器传入参数为资源,输出资源是否健康。
* @param checker 检查器
* @return this
*/
public PriorityFailoverBuilder checker(Predicate checker) {
requireNonNull(checker);
config.setChecker(checker);
return this;
}
/**
* 设置构造后立即启动后台健康检查任务。
* @param startCheckTaskImmediately 是否立即启动健康检查任务
* @return this
*/
public PriorityFailoverBuilder startCheckTaskImmediately(boolean startCheckTaskImmediately) {
config.setStartCheckTaskImmediately(startCheckTaskImmediately);
return this;
}
/**
* 是否激活并发度控制,默认false,激活并发度控制后,getOneAvailable/getOneAvailableExclude和success/fail/down必须匹配,
* 否则并发度计算会出错。
* @param concurrencyControl 激活并发度控制
* @return this
*/
public PriorityFailoverBuilder concurrencyControl(boolean concurrencyControl) {
config.setConcurrencyControl(concurrencyControl);
return this;
}
/**
* 手工并发度控制,开启后getOneAvailable/getOneAvailableExclude不增加并发度,success/fail/down不减少并发度。
* @param manualConcurrencyControl 是否激活手动并发度控制
* @see PriorityFailover#incrConcurrency(Object)
* @see PriorityFailover#decrConcurrency(Object)
* @return this
*/
public PriorityFailoverBuilder manualConcurrencyControl(boolean manualConcurrencyControl) {
config.setManualConcurrencyControl(manualConcurrencyControl);
return this;
}
/**
* 启用AliasMethod算法的资源数量阈值,AliasMethod算法是O(1),但是如果资源总数少,是没有收益的,默认值是10。
* @param aliasMethodThreshold 启用AliasMethod算法的资源数量阈值
* @return this
*/
public PriorityFailoverBuilder aliasMethodThreshold(int aliasMethodThreshold) {
config.setAliasMethodThreshold(aliasMethodThreshold);
return this;
}
/**
* 代表资源的配置maxWeight/minWeight/priority/initWeight,这是个不可变对象。
*/
public static final class ResConfig implements Cloneable {
private final int priority;
private final double maxWeight;
private final double minWeight;
private final double initWeight;
public ResConfig() {
this(DEFAULT_MAX_WEIGHT);
}
public ResConfig(double maxWeight) {
this(maxWeight, DEFAULT_MIN_WEIGHT);
}
public ResConfig(double maxWeight, double minWeight) {
this(maxWeight, minWeight, DEFAULT_PRIORITY);
}
public ResConfig(double maxWeight, double minWeight, int priority) {
this(maxWeight, minWeight, priority, maxWeight);
}
public ResConfig(double maxWeight, double minWeight, int priority, double initWeight) {
this.maxWeight = maxWeight;
this.minWeight = minWeight;
this.priority = priority;
this.initWeight = initWeight;
}
@Override
public ResConfig clone() {
try {
return (ResConfig) super.clone();
} catch (CloneNotSupportedException e) {
throw new RuntimeException(e);
}
}
public int getPriority() {
return priority;
}
public double getMaxWeight() {
return maxWeight;
}
public double getMinWeight() {
return minWeight;
}
public double getInitWeight() {
return initWeight;
}
}
static class PriorityFailoverConfig implements Cloneable {
private Map resources = new HashMap<>();
private String name;
private double priorityFactor = 1.4;
private WeightFunction weightFunction = new RatioWeightFunction<>();
@Nullable
private WeightListener weightListener;
private boolean concurrencyControl = false;
private boolean manualConcurrencyControl = false;
private Duration checkDuration = Duration.ofSeconds(1);
private ScheduledExecutorService checkExecutor = SharedCheckExecutorHolder.getInstance();
@Nullable
private Predicate checker;
private boolean startCheckTaskImmediately;
private int aliasMethodThreshold = 10;
@Override
@SuppressWarnings("unchecked")
protected PriorityFailoverConfig clone() {
try {
PriorityFailoverConfig newOne = (PriorityFailoverConfig) super.clone();
newOne.resources = new HashMap<>(resources);
return newOne;
} catch (CloneNotSupportedException e) {
// assert false
throw new RuntimeException(e);
}
}
public Map getResources() {
return resources;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public double getPriorityFactor() {
return priorityFactor;
}
public void setPriorityFactor(double priorityFactor) {
this.priorityFactor = priorityFactor;
}
public WeightFunction getWeightFunction() {
return weightFunction;
}
public void setWeightFunction(WeightFunction weightFunction) {
this.weightFunction = weightFunction;
}
@Nullable
public WeightListener getWeightListener() {
return weightListener;
}
public void setWeightListener(@Nullable WeightListener weightListener) {
this.weightListener = weightListener;
}
public boolean isConcurrencyControl() {
return concurrencyControl;
}
public void setConcurrencyControl(boolean concurrencyControl) {
this.concurrencyControl = concurrencyControl;
}
public boolean isManualConcurrencyControl() {
return manualConcurrencyControl;
}
public void setManualConcurrencyControl(boolean manualConcurrencyControl) {
this.manualConcurrencyControl = manualConcurrencyControl;
}
public Duration getCheckDuration() {
return checkDuration;
}
public void setCheckDuration(Duration checkDuration) {
this.checkDuration = checkDuration;
}
public ScheduledExecutorService getCheckExecutor() {
return checkExecutor;
}
public void setCheckExecutor(ScheduledExecutorService checkExecutor) {
this.checkExecutor = checkExecutor;
}
@Nullable
public Predicate getChecker() {
return checker;
}
public void setChecker(@Nullable Predicate checker) {
this.checker = checker;
}
public boolean isStartCheckTaskImmediately() {
return startCheckTaskImmediately;
}
public void setStartCheckTaskImmediately(boolean startCheckTaskImmediately) {
this.startCheckTaskImmediately = startCheckTaskImmediately;
}
public int getAliasMethodThreshold() {
return aliasMethodThreshold;
}
public void setAliasMethodThreshold(int aliasMethodThreshold) {
this.aliasMethodThreshold = aliasMethodThreshold;
}
}
}