实现的意义就是就集群的服务器的性能、配置可能不一样,一般都默认轮询的负载均衡的模式,但是这样会造成性能好的服务器工作的少,而性能差的服务器负荷过重,造成一种极为不公平的现象,所以我们就采用权重实现负载均衡,能者多劳。
如下,我们发现在nacos的集群中,有权重的配置,但是配置了,并没有生效(自己到网关中测试)。
直接上实现的代码
/**
* @author ly
*/
@Configuration
public class NacosWeightRule extends AbstractLoadBalancerRule {
private static final Log log = LogFactory.getLog(NacosWeightRule.class);
@Autowired
private NacosDiscoveryProperties discoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig clientConfig) {
}
@Override
public Server choose(Object key) {
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
//服务名称
String name = loadBalancer.getName();
try {
//组名称
String groupName = discoveryProperties.getGroup();
Instance instance = discoveryProperties.namingServiceInstance()
.selectOneHealthyInstance(name, groupName);
log.info("选中的instance = {}" + instance);
/*
* instance转server的逻辑参考自:
* org.springframework.cloud.alibaba.nacos.ribbon.NacosServerList.instancesToServerList
*/
return new NacosServer(instance);
} catch (NacosException e) {
e.printStackTrace();
return null;
}
}
这个类是是nacos实现的,通过进入源码会发现,其实和上边最后走的一段权重代码。
进入 selectOneHealthyInstance
>Balancer.RandomByWeight.selectHost
代码中,会看到getHostByRandomWeight
方法
refresh:
double[] exactWeights = new double[items.size()];
int index = 0;
for (Pair<T> item : itemsWithWeight) {
double singleWeight = item.weight();
//ignore item which weight is zero.see test_randomWithWeight_weight0 in ChooserTest
if (singleWeight <= 0) {
continue;
}
exactWeights[index++] = singleWeight / originWeightSum;
}
weights = new double[items.size()];
double randomRange = 0D;
for (int i = 0; i < index; i++) {
weights[i] = randomRange + exactWeights[i];
randomRange += exactWeights[i];
}
这个方法会将集群的节点,按照权重的比例存到Chooser的weights中,0的位置第一个节点的权重比例,第二个为node1+node2的权重比例,最后一个位置是1
randomWithWeight:
public T randomWithWeight() {
Ref<T> ref = this.ref;
double random = ThreadLocalRandom.current().nextDouble(0, 1);
int index = Arrays.binarySearch(ref.weights, random);
if (index < 0) {
index = -index - 1;
} else {
return ref.items.get(index);
}
if (index >= 0 && index < ref.weights.length) {
if (random < ref.weights[index]) {
return ref.items.get(index);
}
}
/* This should never happen, but it ensures we will return a correct
* object in case there is some floating point inequality problem
* wrt the cumulative probabilities. */
return ref.items.get(ref.items.size() - 1);
}
重要的方法
ThreadLocalRandom.current().nextDouble(0, 1)
生成 一个随机数,范围是0-1(不包括1)https://blog.csdn.net/qpzkobe/article/details/78897762
https://blog.csdn.net/qq_39662660/article/details/88396259
NamingUtils
类中 getGroupedName
用到了这个方法,记录下来
@Override
public Instance selectOneHealthyInstance(String serviceName, String groupName, List<String> clusters, boolean subscribe) throws NacosException {
if (subscribe) {
return Balancer.RandomByWeight.selectHost(
hostReactor.getServiceInfo(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ",")));
} else {
return Balancer.RandomByWeight.selectHost(
hostReactor.getServiceInfoDirectlyFromServer(NamingUtils.getGroupedName(serviceName, groupName), StringUtils.join(clusters, ",")));
}
}
public static String getGroupedName(String serviceName, String groupName) {
StringBuilder resultGroupedName = new StringBuilder()
.append(groupName)
.append(Constants.SERVICE_INFO_SPLITER)
.append(serviceName);
return resultGroupedName.toString().intern();
}