Motan RPC框架中的六个负载均衡算法预览
6个负载均衡算法的总结:
RandomLoadBalance
- 负载随机分配
- ThreadLocalRandom提高多线程并发随机数性能
RoundRobinLoadBalance
- 负载轮流分配
- AtomicInteger idx 保存状态
ActiveWeightLoadBalance
- 低并发优先
- 避免O(N)查找极值
LocalFirstLoadBalance
- IP筛选 本机优先
- 退化为ActiveWeightLoadBalance
ConsistentHashLoadBalance
- 对参数哈希
- 虚拟节点扩充
ConfigurableWeightLoadBalance
- 按分组配置权重,组内RoundRobinLoadBalance
- 未配置权重则退化为RandomLoadBalance
用到的设计模式
使用SPI的策略模式
模板方法模式
AbstractLoadBalance抽象类固定流程,扩展点通过抽象方法留给子类实现
固定流程:
@Override
public Referer<T> select(Request request) {
List<Referer<T>> referers = this.referers;
if (referers == null) {
throw new MotanServiceException(this.getClass().getSimpleName() + " No available referers for call request:" + request);
}
Referer<T> ref = null;
if (referers.size() > 1) {
ref = doSelect(request);
} else if (referers.size() == 1) {
ref = referers.get(0).isAvailable() ? referers.get(0) : null;
}
if (ref != null) {
return ref;
}
throw new MotanServiceException(this.getClass().getSimpleName() + " No available referers for call request:" + request);
}
扩展点留出:
protected abstract Referer<T> doSelect(Request request);
protected abstract void doSelectToHolder(Request request, List<Referer<T>> refersHolder);
负载均衡顶层接口
@Spi(scope = Scope.PROTOTYPE)
public interface LoadBalance<T> {
void onRefresh(List<Referer<T>> referers);
Referer<T> select(Request request);
void selectToHolder(Request request, List<Referer<T>> refersHolder);
void setWeightString(String weightString);
}
- onRefresh:更新服务提供者
- select:按照负载均衡算法选择具体的服务提供者
- selectToHolder:按照负载均衡算个从服务提供者列表中按序批量选取服务提供者
- setWeightString:设置权重信息
随机负载均衡算法:RandomLoadBalance
核心思想:从服务提供者列表中随机选取一个提供服务,Random 使用 AtomicLong CAS (compare-and-set)操作来更新它的seed,CAS在资源高度竞争时存在性能问题。RandomLoadBalance算法使用了ThreadLocalRandom随机类规避了Random在多线程激烈竞争下的性能问题。
ThreadLocalRandom设计有两个要点:一是通过seed是线程隔离的,不存在竞争,二是通过缓存行填充手段避免伪共享问题。
@SpiMeta(name = "random")
public class RandomLoadBalance<T> extends AbstractLoadBalance<T> {
@Override
protected Referer<T> doSelect(Request request) {
List<Referer<T>> referers = getReferers();
int idx = (int) (ThreadLocalRandom.current().nextDouble() * referers.size());
for (int i = 0; i < referers.size(); i++) {
Referer<T> ref = referers.get((i + idx) % referers.size());
if (ref.isAvailable()) {
return ref;
}
}
return null;
}
@Override
protected void doSelectToHolder(Request request, List<Referer<T>> refersHolder) {
List<Referer<T>> referers = getReferers();
int idx = (int) (ThreadLocalRandom.current().nextDouble() * referers.size());
for (int i = 0; i < referers.size(); i++) {
Referer<T> referer = referers.get((i + idx) % referers.size());
if (referer.isAvailable()) {
refersHolder.add(referer);
}
}
}
}
轮询负载均衡算法:RoundRobinLoadBalance
核心思想:服务提供者轮流提供服务,因为要轮流提供服务,所以该算法是有状态的,需要记录上次服务提供者。RoundRobinLoadBalance算法中使用AtomicInteger类型的idx变量记录服务提供者的服务顺序,依次递增。
@SpiMeta(name = "roundrobin")
public class RoundRobinLoadBalance<T> extends AbstractLoadBalance<T> {
private AtomicInteger idx =