SpringCloud之LoadBalancer自定义负载均衡算法,基于nacos权重

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算法核心源码

java 复制代码
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可以配置不同实例的权重信息,可以在

  1. yaml中配置spirng.cloud.nacos.discovery.weight 数值范围从1-100 ,默认为1
  2. 可以在nacos面板找到该实例信息,并实时配置实例的权重

基于nacos权重实现自定义负载

权重:数值越高,代表被选取的概率越大.

根据RoundRobin源码,自定义NacosWeightLoadBalancer

java 复制代码
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;
    }

    @Override
    public 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

java 复制代码
public class WeightLoadBalanceConfiguration {
    @Bean
    public ReactorLoadBalancer<ServiceInstance> weightLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {
        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
        return new NacosWeightLoadBalancer(loadBalancerClientFactory
                .getLazyProvider(name, ServiceInstanceListSupplier.class), name);
    }
}

修改主类配置

java 复制代码
@LoadBalancerClients({
        @LoadBalancerClient(name = "loadbalance-provider-service", configuration = WeightLoadBalanceConfiguration.class)
})
相关推荐
BothSavage3 小时前
Trae远程开发中DeepSeek自定义模型4054错误的排查与修复
算法
小林ixn3 小时前
从暴力到KMP:一道题彻底搞懂字符串匹配的前世今生
算法
烬羽4 小时前
字符串算法入门:从反转字符串到回文判断,面试不再慌
算法·面试
先吃饱再说20 小时前
判断回文字符串,从一行代码到双指针优化
算法
黄敬峰1 天前
深入理解算法核心:从递归思想、数组扁平化到快速排序
算法
得物技术1 天前
从狂野代码到按目标生产:得物推荐 AI Harness 的工程化实践|AICon 演讲整理
人工智能·算法·架构
AI小老六1 天前
SkillOpt 架构拆解:把 Skill 文本当参数,用执行轨迹训练 Agent
后端·算法·ai编程
胡萝卜术1 天前
从“分数打架”到“排名投票”:为什么你的ChatBI必须用RRF?
算法·设计模式·面试
Asize1 天前
初识DFS 与 BFS:递归、队列与图遍历
算法