LoadBalancer基于Nacos权重自定义负载算法
ReactorLoadBalancer接口,实现自定义负载算法需要实现该接口,并实现choose
逻辑,选取对应的节点
public interface ReactorLoadBalancer<T> extends ReactiveLoadBalancer<T> {Mono<Response<T>> choose(Request request);default Mono<Response<T>> choose() {return this.choose(REQUEST);}
}
RoundRobin算法核心源码
private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " + this.serviceId);}return new EmptyResponse();} else {//通过cas的position变量自增,循环 % 实例数。int pos = Math.abs(this.position.incrementAndGet());ServiceInstance instance = (ServiceInstance)instances.get(pos % instances.size());return new DefaultResponse(instance);}}
nacos权重
nacos可以配置不同实例的权重信息,可以在
- yaml中配置
spirng.cloud.nacos.discovery.weight
数值范围从1-100 ,默认为1 - 可以在nacos面板找到该实例信息,并实时配置实例的权重
基于nacos权重实现自定义负载
权重:数值越高,代表被选取的概率越大.
根据RoundRobin
源码,自定义NacosWeightLoadBalancer
package cn.axj.loadbalancer;import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.DefaultResponse;
import org.springframework.cloud.client.loadbalancer.EmptyResponse;
import org.springframework.cloud.client.loadbalancer.Request;
import org.springframework.cloud.client.loadbalancer.Response;
import org.springframework.cloud.loadbalancer.core.*;
import reactor.core.publisher.Mono;import java.util.*;
import java.util.concurrent.ThreadLocalRandom;/*** 基于nacos权重的负载均衡
*/
public class NacosWeightLoadBalancer implements ReactorServiceInstanceLoadBalancer {private static final Log log = LogFactory.getLog(NacosWeightLoadBalancer.class);private final String serviceId;private ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;//nacos权重获取名称,在nacos元数据中private static final String NACOS_WEIGHT_NAME = "nacos.weight";public NacosWeightLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {this.serviceId = serviceId;this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;}@Overridepublic Mono<Response<ServiceInstance>> choose(Request request) {ServiceInstanceListSupplier supplier = this.serviceInstanceListSupplierProvider.getIfAvailable(NoopServiceInstanceListSupplier::new);return supplier.get(request).next().map((serviceInstances) -> {return this.processInstanceResponse(supplier, serviceInstances);});}private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {((SelectedInstanceCallback)supplier).selectedServiceInstance(serviceInstanceResponse.getServer());}return serviceInstanceResponse;}private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {if (instances.isEmpty()) {if (log.isWarnEnabled()) {log.warn("No servers available for service: " + this.serviceId);}} else {//根据权重选择实例,权重高的被选中的概率大//nacos.weight的值越大,被选中的概率越大Double totalWeight = 0D;for (ServiceInstance instance : instances) {String s = instance.getMetadata().get(NACOS_WEIGHT_NAME);double weight = Double.parseDouble(s);totalWeight = totalWeight + weight;//放置当前实例的权重区间instance.getMetadata().put("weight",String.valueOf(totalWeight));}//随机获取一个区间类的数值,nacos权重越大,区间越大,则随机数值落到相应的区间的概率是由区间的大小来决定的。double index = ThreadLocalRandom.current().nextDouble(totalWeight);//根据权重区间选择实例for (ServiceInstance instance : instances) {double weight = Double.parseDouble(instance.getMetadata().get("weight"));if (index <= weight) {return new DefaultResponse(instance);}}}return new EmptyResponse();}
}
配置使用
增加WeightLoadBalanceConfiguration
public class WeightLoadBalanceConfiguration {@Beanpublic ReactorLoadBalancer<ServiceInstance> weightLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);return new NacosWeightLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class), name);}
}
修改主类配置
@LoadBalancerClients({@LoadBalancerClient(name = "loadbalance-provider-service", configuration = WeightLoadBalanceConfiguration.class)
})