网站建设定制开发Spring Cloud Nacos 2021 放弃Ribbon后 使用LoadBalancer + Nacos做负载均衡 实现同集群优先调用以及权重算法

网站建设定制开发最近在倒腾SpringBoot网站建设定制开发整合最新版,网站建设定制开发暂时发现以下几个问题

  1. nacos 2021 网站建设定制开发版本已经没有自带ribbon的整合,网站建设定制开发所以需要引入另一个支持的jar包 loadbalancer
  2. nacos 2021 网站建设定制开发版本已经取消了对ribbon的支持,所以无法通过修改Ribbon负载均衡的模式来实现nacos提供的负载均衡模式

以上仅为个人观点,下面为我的实现方式

第一个问题,使用nacos 2021.1 版本实现

nacos最新版 2021.1版本中

<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>2021.1</version></dependency>
  • 1
  • 2
  • 3
  • 4
  • 5

这个包已经不提供支持,需要引入另一个jar包

<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-loadbalancer</artifactId></dependency>
  • 1
  • 2
  • 3
  • 4

使用这个包来实现负载均衡

具体实现方式和先前版本一样

 @Bean @LoadBalanced //负载均衡注解 public RestTemplate restTemplate(){     return new RestTemplate(); }
  • 1
  • 2
  • 3
  • 4
  • 5

注册bean的同时,添加LoadBalanced负载均衡注解,到这一步为止,可以实现基本的负载均衡功能,负载均衡默认配置为轮询配置

那么接下来,第二个问题,需要实现基于Nacos的负载均衡模式,需要通过自定义loadbalancer负载均衡的方式进行实现,以下内容是经过查阅多方文档进行整合,最终使用自己的代码实现的内容

1.首先需要实现 loadbalancer 自定义 负载均衡模式进行注入

一共有两种写法,可以直接在Spring配置文件中注入Bean,但是这样的话,在 LoadBalancerClients 提供的类里需要写为Spring的配置文件类

import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.core.env.Environment;import org.springframework.web.client.RestTemplate;/** * @program: my-town * @author: 洛天 * @create: 2021-12-13 16:27 **/@Configuration@LoadBalancerClients(defaultConfiguration = {SpringBeanConfiguration.class})public class SpringBeanConfiguration {    @Bean    @LoadBalanced    public RestTemplate restTemplate(){        return new RestTemplate();    }    @Bean    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,                                                            LoadBalancerClientFactory loadBalancerClientFactory) {        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);        return new NacosSameClusterWeightedRule(loadBalancerClientFactory                .getLazyProvider(name, ServiceInstanceListSupplier.class),                name);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

或,新建一个类

import org.springframework.cloud.client.ServiceInstance;import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;import org.springframework.context.annotation.Bean;import org.springframework.core.env.Environment;//这里不用写Configurationpublic class NacosSameClusterConfiguration{    @Bean    ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment,                                                            LoadBalancerClientFactory loadBalancerClientFactory) {        String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);       	// 返回内容为自定义负载均衡的配置类        return new NacosSameClusterWeightedRule(loadBalancerClientFactory                .getLazyProvider(name, ServiceInstanceListSupplier.class),                name);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
import org.springframework.cloud.client.loadbalancer.LoadBalanced;import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClients;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.web.client.RestTemplate;/** * @program: my-town * @author: 洛天 * @create: 2021-12-13 16:27 **/@Configuration// 在这里配置我们自定义的LoadBalancer策略,注:这里的类为注入Bean的类,而非负载均衡的实现类@LoadBalancerClients(defaultConfiguration = {NacosSameClusterConfiguration.class})public class SpringBeanConfiguration {    @Bean    @LoadBalanced    public RestTemplate restTemplate(){        return new RestTemplate();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

接下来,就是实现自定义的负载均衡,基于nacos的 同集群优先调用 以及 基于权重调用 思想实现

以下代码参考部分网络文档,以及官方的RandomLoadBalancer实现类

import java.math.BigDecimal;import java.util.*;import java.util.stream.Collectors;import com.alibaba.cloud.nacos.NacosDiscoveryProperties;import com.alibaba.nacos.api.naming.pojo.Instance;import com.alibaba.nacos.api.utils.StringUtils;import com.alibaba.nacos.client.naming.core.Balancer;import lombok.extern.slf4j.Slf4j;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.NoopServiceInstanceListSupplier;import org.springframework.cloud.loadbalancer.core.ReactorServiceInstanceLoadBalancer;import org.springframework.cloud.loadbalancer.core.SelectedInstanceCallback;import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;import reactor.core.publisher.Mono;import javax.annotation.Resource;@Slf4j// 自定义负载均衡实现需要实现 ReactorServiceInstanceLoadBalancer 接口 以及重写choose方法public class NacosSameClusterWeightedRule implements ReactorServiceInstanceLoadBalancer {    // 注入当前服务的nacos的配置信息    @Resource    private NacosDiscoveryProperties nacosDiscoveryProperties;    // loadbalancer 提供的访问当前服务的名称    final String serviceId;        // loadbalancer 提供的访问的服务列表    ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider;    public NacosSameClusterWeightedRule(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) {        this.serviceId = serviceId;        this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider;    }/**     * 服务器调用负载均衡时调的放啊     * 此处代码内容与 RandomLoadBalancer 一致     */    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);        });    }        /**     * 对负载均衡的服务进行筛选的方法     * 此处代码内容与 RandomLoadBalancer 一致     */    private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) {        Response<ServiceInstance> serviceInstanceResponse = this.getInstanceResponse(serviceInstances);        if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) {            ((SelectedInstanceCallback)supplier).selectedServiceInstance((ServiceInstance)serviceInstanceResponse.getServer());        }        return serviceInstanceResponse;    }    /**     * 对负载均衡的服务进行筛选的方法     * 自定义     * 此处的 instances 实例列表  只会提供健康的实例  所以不需要担心如果实例无法访问的情况     */    private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) {        if (instances.isEmpty()) {            return new EmptyResponse();        }        // 获取当前服务所在的集群名称        String currentClusterName = nacosDiscoveryProperties.getClusterName();        // 过滤在同一集群下注册的服务 根据集群名称筛选的集合        List<ServiceInstance> sameClusterNameInstList  = instances.stream().filter(i-> StringUtils.equals(i.getMetadata().get("nacos.cluster"),currentClusterName)).collect(Collectors.toList());        ServiceInstance sameClusterNameInst;        if (sameClusterNameInstList.isEmpty()) {            // 如果为空,则根据权重直接过滤所有服务列表            sameClusterNameInst = getHostByRandomWeight(instances);        } else {            // 如果不为空,则根据权重直接过滤所在集群下的服务列表            sameClusterNameInst = getHostByRandomWeight(sameClusterNameInstList);        }        return new DefaultResponse(sameClusterNameInst);    }    private ServiceInstance getHostByRandomWeight(List<ServiceInstance> sameClusterNameInstList){        List<Instance> list = new ArrayList<>();        Map<String,ServiceInstance> dataMap = new HashMap<>();        // 此处将 ServiceInstance 转化为 Instance 是为了接下来调用nacos中的权重算法,由于入参不同,所以需要转换,此处建议打断电进行参数调试,以下是我目前为止所用到的参数,转化为map是为了最终方便获取取值到的服务对象        sameClusterNameInstList.forEach(i->{            Instance ins = new Instance();            Map<String, String> metadata = i.getMetadata();            ins.setInstanceId(metadata.get("nacos.instanceId"));            ins.setWeight(new BigDecimal(metadata.get("nacos.weight")).doubleValue());            ins.setClusterName(metadata.get("nacos.cluster"));            ins.setEphemeral(Boolean.parseBoolean(metadata.get("nacos.ephemeral")));            ins.setHealthy(Boolean.parseBoolean(metadata.get("nacos.healthy")));            ins.setPort(i.getPort());            ins.setIp(i.getHost());            ins.setServiceName(i.getServiceId());            ins.setMetadata(metadata);            list.add(ins);            // key为服务ID,值为服务对象            dataMap.put(metadata.get("nacos.instanceId"),i);        });        // 调用nacos官方提供的负载均衡权重算法        Instance hostByRandomWeightCopy = ExtendBalancer.getHostByRandomWeightCopy(list);                // 根据最终ID获取需要返回的实例对象        return dataMap.get(hostByRandomWeightCopy.getInstanceId());    }}class ExtendBalancer extends Balancer {    /**     * 根据权重选择随机选择一个     */    public static Instance getHostByRandomWeightCopy(List<Instance> hosts) {        return getHostByRandomWeight(hosts);    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132

以上就是目前我对于不使用ribbon,使用loadBalancer时候的解决方案,已经通过结果验证,可以实现只访问同一集群下的服务,如果有其他问题,请在品论区留言告知,我会对文档进行适当的修改

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发