跳至主要內容

SpringCloud集群服务-1

wangdx大约 15 分钟

ServerList 实例列表

ServetList 数据保存

  • 客户端通过 Ribbon 进行微服务调用时,首先要通过 Nacos 注册中心来获取全部的微服务实例列表,而这些 Nacos 数据就会统一的保存在 ServerList 接口实例之中。消费端在进行微服务调用时,会通过此实例列表获取一个实例地址,最终实现微服务的调用。
  • com.netflix.loadbalancer.ServerList 是由 Ribbon 所提供的一个服务接口,该接口会包一个 List 集合,里面保存有当前微服务的所有实例(Server)数据,该数据可以由今有一开发者设置静态内容,或者动态获取,为便于读者理解,下面通过一个静态列表的形式为读者演示 ServerList 接口作用。
1、
package com.netflix.loadbalancer;

public interface ServerList<T extends Server> {

    public List<T> getInitialListOfServers();

    /**
     * Return updated list of servers. This is called say every 30 secs
     * (configurable) by the Loadbalancer's Ping cycle
     *
     */
    public List<T> getUpdatedListOfServers();

}
// https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-netflix-ribbon
implementation group: 'org.springframework.cloud', name: 'spring-cloud-starter-netflix-ribbon', version: '2.2.10.RELEASE'


2、
package com.yootk.consumer.ribbon;

import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ServerList;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.List;
@Component
@Slf4j
public class DefaultServerList implements ServerList { // 自定义的服务列表
    @Override
    public List getInitialListOfServers() { // 初始化服务列表
        return null;
    }
    @Override
    public List getUpdatedListOfServers() { // 获取更新后的服务列表
        List<Server> allServers = new ArrayList<>(); // 定义保存服务列表
        allServers.add(new Server("127.0.0.1", 8001)); // 手工添加列表
        allServers.add(new Server("127.0.0.1", 8002)); // 手工添加列表
        log.info("更新服务列表:{}", allServers);
        return allServers;
    }
}


3、
package com.yootk.test;

import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.loadbalancer.ConfigurationBasedServerList;

public class TestConfigurationBasedServerList { // 李兴华高薪就业编程训练营
    public static void main(String[] args) { // 沐言科技:www.yootk.com
        // 通过字符串定义了所有可以使用的服务实例数据,中间使用“,”分割
        String instances = "10.9.19.1:8001,10.9.19.2:8002,10.9.19.3:8003";
        DefaultClientConfigImpl clientConfig = new DefaultClientConfigImpl(); // 默认的IClientConfig接口
        clientConfig.set(CommonClientConfigKey.ListOfServers, instances); // 实例数据
        ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
        serverList.initWithNiwsConfig(clientConfig); // 初始化配置
        System.out.println(serverList.getUpdatedListOfServers());
    }
}

spring-cloud-commons

spring Cloud:负载均衡 - Spring Cloud Loadbalancer 原理open in new window

ILoadBalancer 负载均衡器

1、
package com.yootk.test;

import com.netflix.loadbalancer.LoadBalancerStats;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;

import java.util.ArrayList;
import java.util.List;

public class TestZoneAwareLoadBalancer {
    public static void main(String[] args) {
        List<Server> serverList = new ArrayList<>();// 是直接保存Server集合
        serverList.add(createServer("muyan", "muyan.provider-dept", 8001));
        serverList.add(createServer("muyan", "muyan.provider-dept", 8002));
        serverList.add(createServer("yootk", "yootk.provider-dept", 8001));
        // 设置完成了区域数据之后,下面就需要进行负载均衡算法的配置
        ZoneAwareLoadBalancer loadBalancer = new ZoneAwareLoadBalancer();// 区域负载均衡
        loadBalancer.addServers(serverList); // 添加服务集合
        LoadBalancerStats stats = loadBalancer.getLoadBalancerStats(); // 获取统计数据
        stats.updateServerList(serverList); // 服务列表更新
        stats.getServerStats().keySet().forEach(currentServer -> {
            if (currentServer.getZone().equals("yootk")) {  // 区域算法
                loadBalancer.markServerDown(currentServer); // 服务下线
            }
        });
        for (int x = 0; x < loadBalancer.getServerCount(true); x++) {
            System.out.println(loadBalancer.chooseServer(null)); // 服务筛选
        }
    }

    public static Server createServer(String zone, String url, int index) {
        Server server = new Server(url, index); // 创建实例
        server.setZone(zone); // 设置区域
        return server;
    }
}

ServerListUpdater 服务列表更新

1、
package com.yootk.test;

import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.loadbalancer.ConfigurationBasedServerList;
import com.netflix.loadbalancer.PollingServerListUpdater;
import com.netflix.loadbalancer.ServerListUpdater;
import com.netflix.loadbalancer.ZoneAwareLoadBalancer;

import java.util.concurrent.TimeUnit;

public class TestServerListUpdater {
    public static void main(String[] args) throws Exception {
        // 实例数据,通过Nacos抓取到的信息也就是以下的内容
        String instans = "10.0.19.1:8001,10.9.15.2:8002,10.8.13.5:8003";
        DefaultClientConfigImpl clientConfig = new DefaultClientConfigImpl();
        clientConfig.set(CommonClientConfigKey.ListOfServers, instans); // 保存实例数据
        ConfigurationBasedServerList basedServerList = new ConfigurationBasedServerList();
        basedServerList.initWithNiwsConfig(clientConfig); // 通过客户端配置类实现服务列表初始化
        ServerListUpdater updater = new PollingServerListUpdater(); // 服务列表更新
        updater.start(new ServerListUpdater.UpdateAction() { // 直接编写一个调用
            @Override
            public void doUpdate() {
                System.out.println("【实例更新】最后更新时间:" + updater.getLastUpdate() +
                        "、上次更新间隔时长:" + updater.getDurationSinceLastUpdateMs() +
                        "、错误更新的周期数量:" + updater.getNumberMissedCycles() +
                        "、使用线程数量:" + updater.getCoreThreads());
                // 可以在此处实现数据的更新抓取(依然要通过Nacos完成)
            }
        });
        ZoneAwareLoadBalancer loadBalancer = new ZoneAwareLoadBalancer();
        loadBalancer.setServerListImpl(basedServerList);
        loadBalancer.setServerListUpdater(updater); // 服务列表更新器
        TimeUnit.SECONDS.sleep(Long.MAX_VALUE);
    }
}

ServerListFilter 实例过滤器

1、
package com.yootk.test;

import com.netflix.client.config.DefaultClientConfigImpl;
import com.netflix.client.config.IClientConfigKey;
import com.netflix.config.ConfigurationManager;
import com.netflix.config.DeploymentContext;
import com.netflix.loadbalancer.Server;
import com.netflix.loadbalancer.ZoneAffinityServerListFilter;

import java.util.ArrayList;
import java.util.List;

public class TestServerListFilter {
    public static void main(String[] args) throws Exception {
        List<Server> serverList = new ArrayList<>();// 保存服务实例
        serverList.add(createServer("muyan", "muyan.provider-dept", 8001));
        serverList.add(createServer("muyan", "muyan.provider-dept", 8002));
        serverList.add(createServer("yootk", "yootk.provider-dept", 8001));
        // 所有与Ribbon有关的配置项都是通过IClientConfig接口定义的(application.yml)
        DefaultClientConfigImpl clientConfig = new DefaultClientConfigImpl();
        clientConfig.set(IClientConfigKey.Keys.EnableZoneAffinity, true);// 区域相关性
        clientConfig.set(IClientConfigKey.Keys.EnableZoneExclusivity, true);// 区域独占性
        ConfigurationManager.getDeploymentContext().setValue(
                DeploymentContext.ContextKey.zone, "muyan"); // 区域的优先配置
        ZoneAffinityServerListFilter filter = new ZoneAffinityServerListFilter(); // 区域可用性的过滤器
        filter.initWithNiwsConfig(clientConfig); // 初始化配置
        System.out.println(filter.getFilteredListOfServers(serverList)); // 过滤处理
    }
    public static Server createServer(String zone, String url, int index) {
        Server server = new Server(url, index); // 创建实例
        server.setZone(zone); // 设置区域
        return server;
    }
}

IPing 存活检查

1、
package com.yootk.test;

import com.netflix.loadbalancer.IPing;
import com.netflix.loadbalancer.PingUrl;
import com.netflix.loadbalancer.Server;

public class TestIPing {
    public static void main(String[] args) {
        String host = "provider-dept-8001"; // 服务地址
        String uri = "/provider/dept/list"; // 服务路径
        IPing ping = new PingUrl(false, uri);
        // 按照正常的设计来讲应该使用的是NacosServer
        Server server = new Server(host, 8001); // 偷懒
        System.out.println(ping.isAlive(server)); // 测试一下服务
    }
}

IRule 负载均衡算法

1、
dept.provider: # 微服务的ID
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # IRule子类

2、
dept.provider: # 微服务的ID
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # IRule子类
    NFLoadBalancerClassName: com.netflix.loadbalancer.DynamicServerListLoadBalancer # ILoadBalancer

3、
@RibbonClient(name = "dept.provider", configuration = DeptProviderRibbonConfig.class)

4、
package muyan.yootk.config.ribbon; // 该包不在应用程序启动类的扫描包路径下

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // 这个类必须使用该注解定义
public class DeptProviderRibbonConfig {
    @Autowired
    private IClientConfig clientConfig;
    @Autowired
    private ServerList serverList;
    @Autowired
    private ServerListUpdater updater;
    @Bean // Bean注册
    public IRule ribbonRule() { // 自定义负载均衡规则
        return new RandomRule(); // 随机读取
    }
    @Bean
    public ILoadBalancer loadBalancer() {
        DynamicServerListLoadBalancer loadBalancer = new DynamicServerListLoadBalancer<>(this.clientConfig);
        loadBalancer.setServerListImpl(this.serverList);
        loadBalancer.setServerListUpdater(this.updater);
        loadBalancer.setRule(this.ribbonRule()); // 算法
        return loadBalancer;
    }
}

Ribbon 负载均衡策略

1、
dept.provider: # 微服务的ID
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # IRule子类

2、
dept.provider: # 微服务的ID
  ribbon:
    NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # IRule子类
    NFLoadBalancerClassName: com.netflix.loadbalancer.DynamicServerListLoadBalancer # ILoadBalancer

3、
@RibbonClient(name = "dept.provider", configuration = DeptProviderRibbonConfig.class)

4、
package muyan.yootk.config.ribbon; // 该包不在应用程序启动类的扫描包路径下

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // 这个类必须使用该注解定义
public class DeptProviderRibbonConfig {
    @Autowired
    private IClientConfig clientConfig;
    @Autowired
    private ServerList serverList;
    @Autowired
    private ServerListUpdater updater;
    @Bean // Bean注册
    public IRule ribbonRule() { // 自定义负载均衡规则
        return new RandomRule(); // 随机读取
    }
    @Bean
    public ILoadBalancer loadBalancer() {
        DynamicServerListLoadBalancer loadBalancer = new DynamicServerListLoadBalancer<>(this.clientConfig);
        loadBalancer.setServerListImpl(this.serverList);
        loadBalancer.setServerListUpdater(this.updater);
        loadBalancer.setRule(this.ribbonRule()); // 算法
        return loadBalancer;
    }
}

Ribbon 执行分析

1、
package org.springframework.cloud.netflix.ribbon;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.util.ArrayList;
import java.util.List;
import com.netflix.client.IClient;
import com.netflix.client.http.HttpRequest;
import com.netflix.ribbon.Ribbon;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.AllNestedConditions;
import org.springframework.boot.autoconfigure.condition.AnyNestedCondition;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.client.actuator.HasFeatures;
import org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.LoadBalancedRetryFactory;
import org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.RestTemplateCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.client.RestTemplate;
@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(
      name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
      AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class, // 饿汉式加载配置
      ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration {
   @Autowired(required = false)
   private List<RibbonClientSpecification> configurations = new ArrayList<>();
   @Autowired
   private RibbonEagerLoadProperties ribbonEagerLoadProperties; // Ribbon饿汉式加载配置属性
   @Bean
   public HasFeatures ribbonFeature() {
      return HasFeatures.namedFeature("Ribbon", Ribbon.class);
   }
   @Bean
   public SpringClientFactory springClientFactory() {
      SpringClientFactory factory = new SpringClientFactory();// 获取工厂类的对象实例
      factory.setConfigurations(this.configurations);
      return factory;
   }
   @Bean
   @ConditionalOnMissingBean(LoadBalancerClient.class)
   public LoadBalancerClient loadBalancerClient() { // 客户端负载均衡处理
      return new RibbonLoadBalancerClient(springClientFactory());
   }
   @Bean
   @ConditionalOnClass(name = "org.springframework.retry.support.RetryTemplate")
   @ConditionalOnMissingBean
   public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(
         final SpringClientFactory clientFactory) { // Ribbon重试的处理工厂
      return new RibbonLoadBalancedRetryFactory(clientFactory);
   }
   @Bean
   @ConditionalOnMissingBean
   public PropertiesFactory propertiesFactory() { // 属性的定义
      return new PropertiesFactory();
   }
   @Bean
   @ConditionalOnProperty("ribbon.eager-load.enabled")
   public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
      return new RibbonApplicationContextInitializer(springClientFactory(),
            ribbonEagerLoadProperties.getClients());// 饿汉式加载配置项
   }
   @Configuration(proxyBeanMethods = false)
   @ConditionalOnClass(HttpRequest.class)
   @ConditionalOnRibbonRestClient
   protected static class RibbonClientHttpRequestFactoryConfiguration { // Ribbon客户端请求配置
      @Autowired
      private SpringClientFactory springClientFactory;
      @Bean
      public RestTemplateCustomizer restTemplateCustomizer(
            final RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory) {
         return restTemplate -> restTemplate
               .setRequestFactory(ribbonClientHttpRequestFactory);
      }
      @Bean
      public RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory() {
         return new RibbonClientHttpRequestFactory(this.springClientFactory);
      }
   }
   @Target({ ElementType.TYPE, ElementType.METHOD })
   @Retention(RetentionPolicy.RUNTIME)
   @Documented
   @Conditional(OnRibbonRestClientCondition.class)
   @interface ConditionalOnRibbonRestClient {
   }
   private static class OnRibbonRestClientCondition extends AnyNestedCondition {
      OnRibbonRestClientCondition() {
         super(ConfigurationPhase.REGISTER_BEAN);
      }
      @Deprecated // remove in Edgware"
      @ConditionalOnProperty("ribbon.http.client.enabled")
      static class ZuulProperty { // Zuul属性配置(已经废弃了)
      }
      @ConditionalOnProperty("ribbon.restclient.enabled")
      static class RibbonProperty {
      }
   }
   static class RibbonClassesConditions extends AllNestedConditions {
      RibbonClassesConditions() {
         super(ConfigurationPhase.PARSE_CONFIGURATION);
      }
      @ConditionalOnClass(IClient.class)
      static class IClientPresent {} // 存在有IClient实例
      @ConditionalOnClass(RestTemplate.class)
      static class RestTemplatePresent {} // 存在RestTempalte实例
      @SuppressWarnings("deprecation")
      @ConditionalOnClass(AsyncRestTemplate.class)
      static class AsyncRestTemplatePresent {} // 存在AsyncRestTemplate实例
      @ConditionalOnClass(Ribbon.class)
      static class RibbonPresent {} // 存在有Ribbon实例
   }
}


2、
package org.springframework.cloud.netflix.ribbon;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.Map;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalancerClient;
import org.springframework.cloud.client.loadbalancer.LoadBalancerRequest;
import org.springframework.util.Assert;
import org.springframework.util.ReflectionUtils;
import static org.springframework.cloud.netflix.ribbon
.RibbonUtils.updateToSecureConnectionIfNeeded;
public class RibbonLoadBalancerClient implements LoadBalancerClient {
   private SpringClientFactory clientFactory; // Spring客户端工厂类,加载配置使用的
   public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {
      this.clientFactory = clientFactory;
   }
   @Override
   public URI reconstructURI(ServiceInstance instance, URI original) {
      Assert.notNull(instance, "instance can not be null");
      String serviceId = instance.getServiceId();// 服务的ID名称
      RibbonLoadBalancerContext context = this.clientFactory
            .getLoadBalancerContext(serviceId);
      URI uri; // 根据服务实例拼凑出完整的访问路径(Nacos里面存在的是访问的基本信息)
      Server server; // 服务实例数据存储
      if (instance instanceof RibbonServer) { // 是否为Ribbon实例
         RibbonServer ribbonServer = (RibbonServer) instance;
         server = ribbonServer.getServer();
         uri = updateToSecureConnectionIfNeeded(original, ribbonServer);
      }
      else { // 不是Ribbon实例
         server = new Server(instance.getScheme(), instance.getHost(),
               instance.getPort());
         IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
         ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
         uri = updateToSecureConnectionIfNeeded(original, clientConfig,
               serverIntrospector, server);
      }
      return context.reconstructURIWithServer(server, uri);
   }
   @Override
   public ServiceInstance choose(String serviceId) {
      return choose(serviceId, null); // 根据服务名称获取一个指定的服务实例
   }
   public ServiceInstance choose(String serviceId, Object hint) { // 最终的实现
      Server server = getServer(getLoadBalancer(serviceId), hint);
      if (server == null) {
         return null;
      }
      return new RibbonServer(serviceId, server, isSecure(server, serviceId),
            serverIntrospector(serviceId).getMetadata(server));
   }
   @Override
   public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
         throws IOException {
      return execute(serviceId, request, null);
   }
   public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
         throws IOException {
      ILoadBalancer loadBalancer = getLoadBalancer(serviceId); // 获取接口实例
      Server server = getServer(loadBalancer, hint); // 加载服务实例信息
      if (server == null) {
         throw new IllegalStateException("No instances available for " + serviceId);
      }
      RibbonServer ribbonServer = new RibbonServer(serviceId, server,
            isSecure(server, serviceId),
            serverIntrospector(serviceId).getMetadata(server));
      return execute(serviceId, ribbonServer, request);
   }
   @Override
   public <T> T execute(String serviceId, ServiceInstance serviceInstance,
         LoadBalancerRequest<T> request) throws IOException {
      Server server = null;
      if (serviceInstance instanceof RibbonServer) {
         server = ((RibbonServer) serviceInstance).getServer();
      }
      if (server == null) {
         throw new IllegalStateException("No instances available for " + serviceId);
      }
      RibbonLoadBalancerContext context = this.clientFactory
            .getLoadBalancerContext(serviceId); // Ribbon负载均衡上下文
      RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);
      try {
         T returnVal = request.apply(serviceInstance); // 发送请求
         statsRecorder.recordStats(returnVal); // 状态的记录
         return returnVal;
      } catch (IOException ex) {
         statsRecorder.recordStats(ex);
         throw ex;
      } catch (Exception ex) {
         statsRecorder.recordStats(ex);
         ReflectionUtils.rethrowRuntimeException(ex);
      }
      return null;
   }
   private ServerIntrospector serverIntrospector(String serviceId) {
      ServerIntrospector serverIntrospector = this.clientFactory.getInstance(serviceId,
            ServerIntrospector.class);
      if (serverIntrospector == null) {
         serverIntrospector = new DefaultServerIntrospector();
      }
      return serverIntrospector;
   }
   private boolean isSecure(Server server, String serviceId) { // 是否为安全调用
      IClientConfig config = this.clientFactory.getClientConfig(serviceId); // 获取配置
      ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
      return RibbonUtils.isSecure(config, serverIntrospector, server);
   }
   // Note: This method could be removed?
   protected Server getServer(String serviceId) {
      return getServer(getLoadBalancer(serviceId), null);
   }
   protected Server getServer(ILoadBalancer loadBalancer) {
      return getServer(loadBalancer, null);
   }
   protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
      if (loadBalancer == null) { // 根据指定的负载均衡器来获取Server
         return null;
      }
      // Use 'default' on a null hint, or just pass it on?
      return loadBalancer.chooseServer(hint != null ? hint : "default");
   }
   protected ILoadBalancer getLoadBalancer(String serviceId) {
      return this.clientFactory.getLoadBalancer(serviceId);
   }
   public static class RibbonServer implements ServiceInstance { // 定义Ribbon服务实例
      private final String serviceId;
      private final Server server;
      private final boolean secure;
      private Map<String, String> metadata;
      public RibbonServer(String serviceId, Server server) {
         this(serviceId, server, false, Collections.emptyMap());
      }
      public RibbonServer(String serviceId, Server server, boolean secure,
            Map<String, String> metadata) {
         this.serviceId = serviceId;
         this.server = server;
         this.secure = secure;
         this.metadata = metadata;
      }
      @Override
      public String getInstanceId() {
         return this.server.getId();
      }
      @Override
      public String getServiceId() {
         return this.serviceId;
      }
      @Override
      public String getHost() {
         return this.server.getHost();
      }
      @Override
      public int getPort() {
         return this.server.getPort();
      }
      @Override
      public boolean isSecure() {
         return this.secure;
      }
      @Override
      public URI getUri() {
         return DefaultServiceInstance.getUri(this);
      }
      @Override
      public Map<String, String> getMetadata() {
         return this.metadata;
      }
      public Server getServer() {
         return this.server;
      }
      @Override
      public String getScheme() {
         return this.server.getScheme();
      }
      @Override
      public String toString() {
         final StringBuilder sb = new StringBuilder("RibbonServer{");
         sb.append("serviceId='").append(serviceId).append('\'');
         sb.append(", server=").append(server);
         sb.append(", secure=").append(secure);
         sb.append(", metadata=").append(metadata);
         sb.append('}');
         return sb.toString();
      }
   }
}

Nacos 权重优先调用

1、
spring:
  application: # 配置应用信息
    name: dept.provider # 是微服务的名称
  cloud: # Cloud配置
    nacos: # Nacos注册中心配置
      discovery: # 发现服务
        weight: 10

2、
package muyan.yootk.loadbalancer.rule;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

// 如果要想进行规则的定义常见的做法是直接使用IRule接口完成
// 推荐的做法是使用IRule接口对应的抽象子类AbstractLoadBalancerRule
@Slf4j
public class NacosWeightRule extends AbstractLoadBalancerRule { // Nacos权重规则
    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties; // Nacos配置属性
    private IClientConfig clientConfig;
    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        this.clientConfig = clientConfig;
    }
    @Override
    public Server choose(Object key) { // 选择服务实例
        BaseLoadBalancer loadBalancer = (BaseLoadBalancer) super.getLoadBalancer(); // 获取负载均衡器
        String name = loadBalancer.getName(); // 调用微服务名称
        // 此时的场景就变为了原生项目的NacosClient组件进行处理的操作了
        NamingService namingService = this.nacosDiscoveryProperties.namingServiceInstance(); // 发现服务
        // 根据指定的服务名称以及分组名称获取服务的实例
        try {
            Instance instance = namingService.selectOneHealthyInstance(name, this.nacosDiscoveryProperties.getGroup());
            // 整个的代码是工作在SpringCloud之中的,所以需要将获取到的Instance对象实例转为Server对象实例
            return new NacosServer(instance); // NacosServer是Server子类
        } catch (NacosException e) {
            log.error("获取Nacos注册的微服务实例出错,异常为:" + e);
            return null;
        }
    }
}


3、
package muyan.yootk.config.ribbon; // 该包不在应用程序启动类的扫描包路径下

import com.netflix.loadbalancer.IRule;
import muyan.yootk.loadbalancer.rule.NacosWeightRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // 这个类必须使用该注解定义
public class DeptProviderRibbonConfig {
    @Bean // Bean注册
    public IRule ribbonRule() { // 自定义负载均衡规则
        return new NacosWeightRule(); // 随机读取
    }
}


4、
package com.yootk.consumer;

import muyan.yootk.config.ribbon.DeptProviderRibbonConfig;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.ribbon.RibbonClient;

@SpringBootApplication
@EnableDiscoveryClient
// 如果此时要有多个配置项,可以使用@RibbonClients注解,该注解可以配置多个@RibbonClient
@RibbonClient(name = "dept.provider", configuration = DeptProviderRibbonConfig.class) // 自定义Ribbon配置
public class StartConsumerApplication { // 沐言科技:www.yootk.com
    public static void main(String[] args) {
        SpringApplication.run(StartConsumerApplication.class, args);
    }
}

Nacos 集群优先调度

1、
package muyan.yootk.loadbalancer.rule;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.ExtendBalancer;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
public class NacosClusterWeightRule extends AbstractLoadBalancerRule {
    // 如果要想按照集群调用,那么首先一定要知道当前消费端的集群名称是什么
    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties; // 注入Nacos发现服务配置项
    @Autowired
    private IClientConfig clientConfig;

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        this.clientConfig = clientConfig;
    }

    @Override
    public Server choose(Object key) { // 核心关键
        BaseLoadBalancer loadBalancer = (BaseLoadBalancer) super.getLoadBalancer();
        // 如果此时没有这个调用的微服务名称,那么是无法实现最终的服务查询的
        String name = loadBalancer.getName();// 获取服务名称
        NamingService namingService = this.nacosDiscoveryProperties.namingServiceInstance();
        // 获取指定服务名称的全部的实例列表数据
        try {
            // 根据指定的服务名称以及分组查询所有健康的服务实例列表,此时的列表包含有所有的集群信息
            List<Instance> instances = namingService.selectInstances(name, this.nacosDiscoveryProperties.getGroup(), true);
            // 理论上现在应该采用的是迭代的处理形式,将全部的集合列表进行迭代处理,随后进行集群名称的判断
            List<Instance> clusterInstance = instances.stream()
                    .filter(instance -> Objects.equals(instance.getClusterName(), this.nacosDiscoveryProperties.getClusterName()))
                    .collect(Collectors.toList());
            List<Instance> instancesChoose = null; // 保存最终的返回的列表
            // 如果以上的处理可以获取指定集群名称下的全部的服务实例数据,那么就可以随意返回一个,但是如果不能够获取?
            if (CollectionUtils.isEmpty(clusterInstance)) { // 此时集群下的实例列表为空
                // 此时在指定集群名称下没有查找到任何的实例列表,所以就把所获取到的全部实例列表
                instancesChoose = instances;
            } else { // 如果已经查找到了指定集群名称下的实例列表
               instancesChoose = clusterInstance; // 保存集群实例列表
            }
            // 因为最终所需要的是一个Server实例信息,所以这个时候可以考虑随机读取一个,或者按照权重返回一个
            Instance selectedInstance = ExtendBalancer.getHostByRandomWeight2(instancesChoose);
            return new NacosServer(selectedInstance); // 指定集群名称下的一个实例
        } catch (NacosException e) {
            log.error("服务实例查询时出现了错误,异常为:{}", e);
        }
        return null;
    }
}


2、
spring:
  application: # 配置应用信息
    name: consumer # 是微服务的名称
  cloud: # Cloud配置
    nacos: # Nacos注册中心配置
      discovery: # 发现服务
        server-addr: nacos-server:8848 # Nacos服务地址
        namespace: 96c23d77-8d08-4648-b750-1217845607ee # 命名空间ID
        group: MICROCLOUD_GROUP # 一般建议大写
        cluster-name: YootkCluster # 配置集群名称
        username: muyan # 用户名
        password: yootk # 密码
        register-enabled: false # 消费端不注册

3、
package muyan.yootk.config.ribbon; // 该包不在应用程序启动类的扫描包路径下

import com.netflix.loadbalancer.IRule;
import muyan.yootk.loadbalancer.rule.NacosClusterWeightRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // 这个类必须使用该注解定义
public class DeptProviderRibbonConfig {
    @Bean // Bean注册
    public IRule ribbonRule() { // 自定义负载均衡规则
        return new NacosClusterWeightRule(); // 随机读取
    }
}


4、
spring:
  application: # 配置应用信息
    name: consumer # 是微服务的名称
  cloud: # Cloud配置
    nacos: # Nacos注册中心配置
      discovery: # 发现服务
        server-addr: nacos-server:8848 # Nacos服务地址
        namespace: 96c23d77-8d08-4648-b750-1217845607ee # 命名空间ID
        group: MICROCLOUD_GROUP # 一般建议大写
        cluster-name: MuyanYootk # 配置集群名称

元数据优先调度

1、
spring:
  application: # 配置应用信息
    name: dept.provider # 是微服务的名称
  cloud: # Cloud配置
    nacos: # Nacos注册中心配置
      discovery: # 发现服务
        weight: 10
        service: ${spring.application.name} # 使用微服务的名称作为注册的服务名称
        server-addr: nacos-server:8848 # Nacos地址
        namespace: 96c23d77-8d08-4648-b750-1217845607ee # 命名空间ID
        group: MICROCLOUD_GROUP # 一般建议大写
        cluster-name: YootkCluster # 配置集群名称
        username: muyan # 用户名
        password: yootk # 密码
        metadata:  # 根据自身的需要配置元数据
          version: 2.0 # 自定义元数据项
          company: 沐言科技 # 自定义元数据项
          url: www.yootk.com # 自定义元数据项
          author: 李兴华(爆可爱的小李老师) # 自定义元数据项

2、
spring:
  application: # 配置应用信息
    name: consumer # 是微服务的名称
  cloud: # Cloud配置
    nacos: # Nacos注册中心配置
      discovery: # 发现服务
        server-addr: nacos-server:8848 # Nacos服务地址
        namespace: 96c23d77-8d08-4648-b750-1217845607ee # 命名空间ID
        group: MICROCLOUD_GROUP # 一般建议大写
        cluster-name: MuyanYootk # 配置集群名称
        username: muyan # 用户名
        password: yootk # 密码
        register-enabled: false # 消费端不注册
        metadata:
          version: 2.0 # 版本编号匹配了

3、

package muyan.yootk.loadbalancer.rule;

import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.ribbon.ExtendBalancer;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.BaseLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
public class NacosVersionRule extends AbstractLoadBalancerRule {
    // 如果要想按照集群调用,那么首先一定要知道当前消费端的集群名称是什么
    @Autowired
    private NacosDiscoveryProperties nacosDiscoveryProperties; // 注入Nacos发现服务配置项
    @Autowired
    private IClientConfig clientConfig;

    @Override
    public void initWithNiwsConfig(IClientConfig clientConfig) {
        this.clientConfig = clientConfig;
    }

    @Override
    public Server choose(Object key) {
        BaseLoadBalancer loadBalancer = (BaseLoadBalancer) super.getLoadBalancer();
        // 如果此时没有这个调用的微服务名称,那么是无法实现最终的服务查询的
        String name = loadBalancer.getName();// 获取服务名称
        NamingService namingService = this.nacosDiscoveryProperties.namingServiceInstance();
        try {
            // 根据指定的服务名称以及分组的名称获取全部的可用实例数据
            List<Instance> instances = namingService.selectInstances(name, this.nacosDiscoveryProperties.getGroup(), true);
            // 对获取到的实例集合进行迭代处理,筛选出所需要的与当前版本匹配的实例数据
            List<Instance> metadataVersionMatchInstance = instances.stream()
                    .filter(instance -> Objects.equals(
                            this.nacosDiscoveryProperties.getMetadata().get("version"), // 消费端配置的元数据版本项
                            instance.getMetadata().get("version"))) // 注册微服务实例配置的元数据版本项
                    .collect(Collectors.toList());
            // 必须考虑没有匹配版本下的实例筛选操作
            List<Instance> selectedInstances = null; // 最终所使用的实例集合
            if (CollectionUtils.isEmpty(metadataVersionMatchInstance)) {    // 没有查询到匹配的集合
                selectedInstances = instances;
            } else {
                selectedInstances = metadataVersionMatchInstance; // 版本匹配
            }
            Instance instance = ExtendBalancer.getHostByRandomWeight2(selectedInstances);
            return new NacosServer(instance);
        } catch (NacosException e) {
            log.error("获取Nacos注册的微服务实例出错,异常为:" + e);
        }
        return null;
    }
}

4、
package muyan.yootk.config.ribbon; // 该包不在应用程序启动类的扫描包路径下

import com.netflix.loadbalancer.IRule;
import muyan.yootk.loadbalancer.rule.NacosVersionRule;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration // 这个类必须使用该注解定义
public class DeptProviderRibbonConfig {
    @Bean // Bean注册
    public IRule ribbonRule() { // 自定义负载均衡规则
        return new NacosVersionRule(); // 随机读取
    }
}

demo


上次编辑于: