nacos 注册中心一个服务,会存在多个实例,这时候网关就不是很清楚最终需要向那个实例节点发送请求,所以需要一个负载均衡测试算法
通过工厂 + 策略模式
当网关执行请求的时候,从自己的注册器,本地缓存获取到了 nacos 的服务元数据信息,一个服务存在多个节点的时候,根据客户端 SDK 服务自己配置的策略方式,进行均衡
BalanceType balanceType = BalanceType.valueOf(serviceDefinition.getRobinType());
Set<ServiceInstance> serviceInstances = instance.getServiceInstanceByUniqueId(gateWayRequest.getUniqueId(), false);
ServiceInstance serviceInstance = BalanceManger.getInstance().getIBalance(balanceType, new ArrayList<>(serviceInstances)).select();
gatewayContext.getRequest().setModifyHost(serviceInstance.getIp() + ":" + serviceInstance.getPort());
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ApiService {
String serviceId();
String version() default "1.0";
ApiProtocol protocol();
String patternPath();
BalanceType robinType() default BalanceType.RANDOM;
}
- 随机均衡
package com.xiaohu.core.loadbalance;
import java.util.List;
import java.util.Random;
/**
* @Version 1.0
* @Author huqiang 随机负载均衡
* @Description RandomRobin
* @Date 2024/4/28 下午1:25
**/
public class RandomRobin<T> implements IBalance<T>{
private List<T> elements;
private final Random random ;
public RandomRobin(List<T> elements) {
random = new Random();
init(elements);
}
@Override
public void init(List<T> elements) {
this.elements = elements;
}
@Override
public T select() {
if (elements == null || elements.isEmpty()) {
throw new IllegalStateException("Cannot select an element from an empty list");
}
// 使用Random.nextInt(int n) 来生成一个介于0(包含)和指定值n(不包含)之间的随机数
int randomIndex = random.nextInt(elements.size());
return elements.get(randomIndex);
}
}
- 轮询均衡
package com.xiaohu.core.loadbalance;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
* @Version 1.0
* @Author huqiang 轮询负载均衡
* @Description RandomRobin
* @Date 2024/4/28 下午1:25
**/
public class RoundRobin<T> implements IBalance<T> {
private List<T> elements;
private AtomicInteger index;
public RoundRobin(List<T> elements) {
init(elements);
}
@Override
public void init(List<T> elements) {
this.elements = elements;
this.index = new AtomicInteger(0);
}
@Override
public T select() {
if (elements == null || elements.isEmpty()) {
throw new IllegalStateException("Cannot select an element from an empty list");
}
int currentIndex = index.getAndUpdate(i -> (i + 1) % elements.size());
return elements.get(currentIndex);
}
}
- 权重均衡
package com.xiaohu.core.loadbalance;
import com.xiaohu.common.config.ServiceInstance;
import org.apache.commons.collections.CollectionUtils;
import java.util.*;
/**
* @Version 1.0
* @Author huqiang
* @Description WeightedRandom 权重算法
* @Date 2024/4/28 下午12:50
**/
public class WeightedRandomRobin<T> implements IBalance<T> {
private class WeightedElement {
double accumulatedWeight;
T object;
public WeightedElement(T object, double accumulatedWeight) {
this.object = object;
this.accumulatedWeight = accumulatedWeight;
}
}
private List<WeightedElement> weightedElements;
private final List<Double> weights = new LinkedList<>();
private double totalWeight;
private Random random;
@SuppressWarnings("unchecked")
public WeightedRandomRobin(List<T> elements,List<Double>...weights) {
T t = elements.get(0);
if (t instanceof ServiceInstance serviceInstance) {
for (int i = 0; i < elements.size(); i++) {
this.weights.add(Double.valueOf(serviceInstance.getWeight()));
}
}else {
this.weights.addAll(weights[0]);
}
if (CollectionUtils.isEmpty(elements) || elements.size() != this.weights.size()) {
throw new IllegalArgumentException("Elements and weights must be the same size");
}
init(elements);
}
@Override
public void init(List<T> elements) {
this.weightedElements = new ArrayList<>(elements.size());
double accumulatedWeight = 0.0;
for (int i = 0; i < elements.size(); i++) {
accumulatedWeight += weights.get(i);
weightedElements.add(new WeightedElement(elements.get(i), accumulatedWeight));
}
this.totalWeight = accumulatedWeight;
this.random = new Random();
}
@Override
public T select() {
double randomWeight = random.nextDouble() * totalWeight;
for (WeightedElement we : weightedElements) {
if (randomWeight <= we.accumulatedWeight) {
return we.object;
}
}
//找不到取第一个
return weightedElements.get(0).object;
}
}
- 随机权重均衡
package com.xiaohu.core.loadbalance;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* @author xiaohugg 随机+轮询算法
* @version 1.0
* @date 2024/4/25 22:10:25
*/
public class RandomRoundRobin<T> implements IBalance<T> {
private List<T> elements;
private List<Integer> indexPool;
public RandomRoundRobin(List<T> elements) {
init(elements);
}
@Override
public void init(List<T> elements) {
this.elements = new ArrayList<>(elements);
this.indexPool = new ArrayList<>(elements.size());
for (int i = 0; i < elements.size(); i++) {
this.indexPool.add(i);
}
}
@Override
public T select() {
if (indexPool.isEmpty()) {
for (int i = 0; i < elements.size(); i++) {
indexPool.add(i);
}
}
Collections.shuffle(indexPool);
Integer index = indexPool.remove(indexPool.size() - 1);
return elements.get(index);
}
}
负载均衡管理器类
package com.xiaohu.core.loadbalance;
import com.xiaohu.common.enums.BalanceType;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
/**
* @Version 1.0
* @Author huqiang
* @Description BalanceManger
* @Date 2024/4/28 下午1:02
**/
public class BalanceManger {
private static final BalanceManger INSTANCE = new BalanceManger();
private final Map<String, Map<BalanceType, IBalance<?>>> balanceMapCache = new ConcurrentHashMap<>();
private BalanceManger() {}
public <T> IBalance<T> getIBalance(BalanceType type, List<T> elements, String serviceId) {
Map<BalanceType, IBalance<?>> serviceBalances = balanceMapCache.computeIfAbsent(serviceId, k -> new ConcurrentHashMap<>());
synchronized (serviceBalances) {
@SuppressWarnings("unchecked")
IBalance<T> balance = (IBalance<T>) serviceBalances.computeIfAbsent(type, t -> createBalance(type, elements));
return balance;
}
}
private <T> IBalance<T> createBalance(BalanceType type, List<T> elements) {
Map<BalanceType, Function<List<T>, IBalance<T>>> balanceMap = getBalanceMap();
Function<List<T>, IBalance<T>> selector = balanceMap.get(type);
if (selector == null) {
throw new IllegalArgumentException("Unsupported balance type: " + type);
}
return selector.apply(elements);
}
@SuppressWarnings("unchecked")
private <T> Map<BalanceType, Function<List<T>, IBalance<T>>> getBalanceMap() {
return Map.of(
BalanceType.RANDOM, RandomRobin::new,
BalanceType.WEIGHT, WeightedRandomRobin::new,
BalanceType.RANDOM_ROUND, RandomRoundRobin::new,
BalanceType.ROUND, RoundRobin::new
);
}
public static BalanceManger getInstance() {
return INSTANCE;
}
}