从源码上学习 SpringCloud - Ribbon 负载均衡组件

1、关于 Ribbon

       Ribbon 是将负载均衡逻辑以代码的形式封装到服务消费者的客户端上,服务消费者客户端维护了一份服务提供者的信息列表,有了信息列表,通过负载均衡策略将请求分摊给多个服务提供者,从而达到负载均衡的目的。
 
当前 Ribbon 用于生产的模块有一下三个:

  • ribbon-loadbalancer : 可以独立使用或与其他模块一起使用的负载均衡器API.
  • ribbon-eureka : Ribbon结合Eureka 客户端的API,为负载均衡器提供动态服务注册列表信息。
  • ribbon-core :Ribbon 的核心 API.
     

LoadBalanceClient:
 
       1、负载均衡器LoadBalanceClient 是从 Eureka Client 获取服务注册列表信息的,并将服务注册列表信息缓存了一份。

       2、在LoadBalancerClient 调用 choose() 方法时,根据负载均衡策略选择一个服务实例的信息,从而进行了负载均衡。

       3、LoadBalancerClient 也可以不从Eureka Client 获取注册列表信息,这时需要自己维护一份服务注册列表信息。
 
自己维护一份服务注册列表信息,需要新建一个服务。在 application.yml 文件中加入如下代码:

stores:
    ribbon:
        # 将example1.com,example2.com这两个服务实例的URL配置到注册列表
        listOfServers:example1.com,example2.com  
ribbon:
    eureka:
         enable: false	# 禁止调用Eureka Client 获取注册列表

2、Ribbon + RestTemplate 消费服务

 
       这里假设已经有一个eureka服务(eureka-server),一个服务提供者(ribbion-server),现在新建一个服务(工程名为:eureka-ribbon-client)。服务提供者(ribbion-server)提供了一个叫 /icao 的API接口。
 
新建eureka-ribbon-client工程:

pom 文件:

        <!-- 注册中心 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-eureka</artifactId>
            <version>1.4.5.RELEASE</version>
        </dependency>
        <!-- 负载均衡组件 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
            <version>2.1.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <!-- 本地需要用spring-boot自带的tomcat容器,而服务器上有tomcat,所以打到服务器上要排除 -->
            <artifactId>spring-boot-starter-web</artifactId>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-tomcat</artifactId>
                </exclusion>
            </exclusions>
        </dependency>

 
application.yml 文件:

spring:
  application:
    name: eureka-ribbon-client
##  eureka 服务注册中心配置
eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/

 
启动类:


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;

@SpringBootApplication
@EnableEurekaClient // 注册服务 开启 EurekaClient 功能
public class EurakaRibbonClientApplication extends SpringBootServletInitializer {
    
    


    public static void main(String[] args) {
    
    
        SpringApplication.run(EurakaRibbonClientApplication .class, args);
    }

}

 
RibbionConfig.java 类:
 
      在程序的IOC容器中注入一个 restTemplate 的 Bean ,并在这个 Bean 上加上 @LoadBalance 注解,此时 RestTemplate 就结合了 Ribbon 开启了负载均衡功能。


import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * @ClassName: RibbionConfig
 * @Description:
 * @author
 * @Date:2020/4/16 23:09
 **/
@Configuration
public class RibbionConfig {
    
    

    @Bean
    @LoadBalanced
    RestTemplate restTemplate() {
    
    
        return new RestTemplate();
    }

}

 
编写调用 ribbon-client 服务接口的业务层和控制层


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

/**
 * @ClassName: RibbonService
 * @Description:
 * @author:
 * @Date:2020/4/16 23:17
 **/
@Service
public class RibbonService {
    
    

    @Autowired
    private RestTemplate restTemplate;
    
    public String callIcao(String name) {
    
    
        // 这里的 eureka-client 是服务名
        return restTemplate.getForObject("http://eureka-client/icao?name="+name, String.class);
    }

}



import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

/**
 * @ClassName: RibbonController
 * @Description:
 * @author: 
 * @Date:2020/4/16 23:22
 **/
@RestController
public class RibbonController {
    
    

     @Autowired
     private RibbonService ribbonService;

     @RequestMapping("/testCallIcaoApi")
     public String testCallIcaoApi(@RequestParam(required = false,
                                    defaultValue = "forezp") String name) {
    
    
        return ribbonService.callIcao(name);
     }


}

3、源码解析 Ribbon

3.1 分析 LoadBalancerClient

 
查看 LoadBalancerClient 的父类和实现类
 
        LoadBalancerClient 是一个接口类,其继承了 ServiceInstanceChooser,它的实现类为 RibbonLoadBalancerClient,其类图关系如下:
在这里插入图片描述
 
分析LoadBalancerClient 类源码
 
       查看LoadBalancerClient 类源码可以发现,其包含三个方法。其中有两个 excute() 方法,均用于执行请求, reconstructURI() 用于重构 Url.
 
LoadBalancerClient 是一个负载均衡的客户端。
 

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cloud.client.loadbalancer;

import java.io.IOException;
import java.net.URI;
import org.springframework.cloud.client.ServiceInstance;

public interface LoadBalancerClient extends ServiceInstanceChooser {
    
    
    <T> T execute(String var1, LoadBalancerRequest<T> var2) throws IOException;

    <T> T execute(String var1, ServiceInstance var2, LoadBalancerRequest<T> var3) throws IOException;

    URI reconstructURI(ServiceInstance var1, URI var2);
}

3.2 分析 ServiceInstanceChooser

 
ServiceInstanceChooser 接口类有一个方法,是根据 serviceId 获取 ServiceInstance,即通过服务名来选择服务实例。
 

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cloud.client.loadbalancer;

import org.springframework.cloud.client.ServiceInstance;

public interface ServiceInstanceChooser {
    
    
    ServiceInstance choose(String var1);
}

3.3 分析 RibbionLoadBalancerClient

 
      LoadBalancerClient 的实现类是 RibbionLoadBalancerClient. 需要知道的是,最终负载均衡的请求处理都是由RibbionLoadBalancerClient类来执行的。分析 RibbionLoadBalancerClient 源码可以看出,choose() 方法用于选择具体服务实例。该方法通过 getServer() 方法来获取实例,追踪原代码不难发现,其最终交给了 ILoadBalancer 类去选择服务实例。

以下是 RibbionLoadBalancerClient 部分源码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cloud.netflix.ribbon;

import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import java.io.IOException;
import java.net.URI;
import java.util.Collections;
import java.util.Map;
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;

public class RibbonLoadBalancerClient implements LoadBalancerClient {
    
    
    
    ……  //  省略代码
    

    public ServiceInstance choose(String serviceId) {
    
    
        Server server = this.getServer(serviceId);
        return server == null ? null : new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
    }
    
    protected Server getServer(String serviceId) {
    
    
        return this.getServer(this.getLoadBalancer(serviceId));
    }
    
    protected Server getServer(ILoadBalancer loadBalancer) {
    
    
        return loadBalancer == null ? null : loadBalancer.chooseServer("default");
    }
    
    protected ILoadBalancer getLoadBalancer(String serviceId) {
    
    
        return this.clientFactory.getLoadBalancer(serviceId);
    }

  
}

3.4 分析 ILoadBalancer

 
分析 ILoadBalancer 类源码,ILoadBalancer 接口方法中的中文注释,由本博主后期加上的。
 

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.netflix.loadbalancer;

import java.util.List;

public interface ILoadBalancer {
    
     

    // 该方法用于添加一个 Server 集合
    void addServers(List<Server> var1);
    
    // 该方法用于根据 Key 去获取 Server
    Server chooseServer(Object var1);

    // 该方法用于标记某个服务下线
    void markServerDown(Server var1);

    /** @deprecated 表示该方法已过时 */
    @Deprecated 
    List<Server> getServerList(boolean var1);

    // 该方法用于获取可用的 Server 集合
    List<Server> getReachableServers();

    // 该方法用于获取所有的 Server 集合
    List<Server> getAllServers();
}

 
ILoadBalancer 类相关类图:在这里插入图片描述
 
       由类图可以看出 ILoadBalancer 类的子类为 BaseLoadBalancer 类,BaseLoadBalancer 的实现类为 DynamicServerListLoadBalancer 类。

3.5 分析 DynamicServerListLoadBalancer

 
分析 DynamicServerListLoadBalancer 源码中的构造方法

public DynamicServerListLoadBalancer(IClientConfig clientConfig,
                                     IRule rule, IPing ping, 
                                     ServerList<T> serverList, 
                                     ServerListFilter<T> filter, 
                                     ServerListUpdater serverListUpdater) {
    
    
           // 省略代码 ……
}

可以看出,DynamicServerListLoadBalancer 需要配置:

  • IClientConfig:用于配置负载均衡的客户端,默认实现类为 DefaultClientConfgImpl.
  • IRule:用于配置负载均衡的策略,IRule有三个方法,其中 choose() 是根据 key 来获取 server 实例的,setLoadBalancer() 和 getLoadBalancer() 是用来设置和获取 ILoadBalancer 的。
  • IPing:用来向 server 发送 “ping”,来判断该 server 是否有响应,从而判断该 server 是否可用。
  • ServerList:是定义获取所有 server 的注册列表信息的接口。
  • ServerListFilter:该接口定义了可根据配置,去过滤或者特性动态地获取符号条件的 server 列表的方法。
  • ServerListUpdater:这个类用于 server 列表的更新。
     
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.netflix.loadbalancer;

import com.google.common.annotations.VisibleForTesting;
import com.netflix.client.ClientFactory;
import com.netflix.client.config.CommonClientConfigKey;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.ServerListUpdater.UpdateAction;
import com.netflix.servo.annotations.DataSourceType;
import com.netflix.servo.annotations.Monitor;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class DynamicServerListLoadBalancer<T extends Server> extends BaseLoadBalancer {
    
    
    private static final Logger LOGGER = LoggerFactory.getLogger(DynamicServerListLoadBalancer.class);
    boolean isSecure;
    boolean useTunnel;
    protected AtomicBoolean serverListUpdateInProgress;
    volatile ServerList<T> serverListImpl;
    volatile ServerListFilter<T> filter;
    protected final UpdateAction updateAction;
    protected volatile ServerListUpdater serverListUpdater;

    public DynamicServerListLoadBalancer() {
    
    
        this.isSecure = false;
        this.useTunnel = false;
        this.serverListUpdateInProgress = new AtomicBoolean(false);
        this.updateAction = new UpdateAction() {
    
    
            public void doUpdate() {
    
    
                DynamicServerListLoadBalancer.this.updateListOfServers();
            }
        };
    }

    /** @deprecated */
    @Deprecated
    public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping, ServerList<T> serverList, ServerListFilter<T> filter) {
    
    
        this(clientConfig, rule, ping, serverList, filter, new PollingServerListUpdater());
    }

    public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping, ServerList<T> serverList, ServerListFilter<T> filter, ServerListUpdater serverListUpdater) {
    
    
        super(clientConfig, rule, ping);
        this.isSecure = false;
        this.useTunnel = false;
        this.serverListUpdateInProgress = new AtomicBoolean(false);
        this.updateAction = new UpdateAction() {
    
    
            public void doUpdate() {
    
    
                DynamicServerListLoadBalancer.this.updateListOfServers();
            }
        };
        this.serverListImpl = serverList;
        this.filter = filter;
        this.serverListUpdater = serverListUpdater;
        if (filter instanceof AbstractServerListFilter) {
    
    
            ((AbstractServerListFilter)filter).setLoadBalancerStats(this.getLoadBalancerStats());
        }

        this.restOfInit(clientConfig);
    }

    public DynamicServerListLoadBalancer(IClientConfig clientConfig) {
    
    
        this.isSecure = false;
        this.useTunnel = false;
        this.serverListUpdateInProgress = new AtomicBoolean(false);
        this.updateAction = new UpdateAction() {
    
    
            public void doUpdate() {
    
    
                DynamicServerListLoadBalancer.this.updateListOfServers();
            }
        };
        this.initWithNiwsConfig(clientConfig);
    }

    public void initWithNiwsConfig(IClientConfig clientConfig) {
    
    
        try {
    
    
            super.initWithNiwsConfig(clientConfig);
            String niwsServerListClassName = clientConfig.getPropertyAsString(CommonClientConfigKey.NIWSServerListClassName, "com.netflix.loadbalancer.ConfigurationBasedServerList");
            ServerList<T> niwsServerListImpl = (ServerList)ClientFactory.instantiateInstanceWithClientConfig(niwsServerListClassName, clientConfig);
            this.serverListImpl = niwsServerListImpl;
            if (niwsServerListImpl instanceof AbstractServerList) {
    
    
                AbstractServerListFilter<T> niwsFilter = ((AbstractServerList)niwsServerListImpl).getFilterImpl(clientConfig);
                niwsFilter.setLoadBalancerStats(this.getLoadBalancerStats());
                this.filter = niwsFilter;
            }

            String serverListUpdaterClassName = clientConfig.getPropertyAsString(CommonClientConfigKey.ServerListUpdaterClassName, "com.netflix.loadbalancer.PollingServerListUpdater");
            this.serverListUpdater = (ServerListUpdater)ClientFactory.instantiateInstanceWithClientConfig(serverListUpdaterClassName, clientConfig);
            this.restOfInit(clientConfig);
        } catch (Exception var5) {
    
    
            throw new RuntimeException("Exception while initializing NIWSDiscoveryLoadBalancer:" + clientConfig.getClientName() + ", niwsClientConfig:" + clientConfig, var5);
        }
    }

    void restOfInit(IClientConfig clientConfig) {
    
    
        boolean primeConnection = this.isEnablePrimingConnections();
        this.setEnablePrimingConnections(false);
        this.enableAndInitLearnNewServersFeature();
        this.updateListOfServers();
        if (primeConnection && this.getPrimeConnections() != null) {
    
    
            this.getPrimeConnections().primeConnections(this.getReachableServers());
        }

        this.setEnablePrimingConnections(primeConnection);
        LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
    }

    public void setServersList(List lsrv) {
    
    
        super.setServersList(lsrv);
        Map<String, List<Server>> serversInZones = new HashMap();
        Iterator var4 = lsrv.iterator();

        while(var4.hasNext()) {
    
    
            Server server = (Server)var4.next();
            this.getLoadBalancerStats().getSingleServerStat(server);
            String zone = server.getZone();
            if (zone != null) {
    
    
                zone = zone.toLowerCase();
                List<Server> servers = (List)serversInZones.get(zone);
                if (servers == null) {
    
    
                    servers = new ArrayList();
                    serversInZones.put(zone, servers);
                }

                ((List)servers).add(server);
            }
        }

        this.setServerListForZones(serversInZones);
    }

    protected void setServerListForZones(Map<String, List<Server>> zoneServersMap) {
    
    
        LOGGER.debug("Setting server list for zones: {}", zoneServersMap);
        this.getLoadBalancerStats().updateZoneServerMapping(zoneServersMap);
    }

    public ServerList<T> getServerListImpl() {
    
    
        return this.serverListImpl;
    }

    public void setServerListImpl(ServerList<T> niwsServerList) {
    
    
        this.serverListImpl = niwsServerList;
    }

    public ServerListFilter<T> getFilter() {
    
    
        return this.filter;
    }

    public void setFilter(ServerListFilter<T> filter) {
    
    
        this.filter = filter;
    }

    public ServerListUpdater getServerListUpdater() {
    
    
        return this.serverListUpdater;
    }

    public void setServerListUpdater(ServerListUpdater serverListUpdater) {
    
    
        this.serverListUpdater = serverListUpdater;
    }

    public void forceQuickPing() {
    
    
    }

    public void enableAndInitLearnNewServersFeature() {
    
    
        LOGGER.info("Using serverListUpdater {}", this.serverListUpdater.getClass().getSimpleName());
        this.serverListUpdater.start(this.updateAction);
    }

    private String getIdentifier() {
    
    
        return this.getClientConfig().getClientName();
    }

    public void stopServerListRefreshing() {
    
    
        if (this.serverListUpdater != null) {
    
    
            this.serverListUpdater.stop();
        }

    }

    @VisibleForTesting
    public void updateListOfServers() {
    
    
        List<T> servers = new ArrayList();
        if (this.serverListImpl != null) {
    
    
            servers = this.serverListImpl.getUpdatedListOfServers();
            LOGGER.debug("List of Servers for {} obtained from Discovery client: {}", this.getIdentifier(), servers);
            if (this.filter != null) {
    
    
                servers = this.filter.getFilteredListOfServers((List)servers);
                LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}", this.getIdentifier(), servers);
            }
        }

        this.updateAllServerList((List)servers);
    }

    protected void updateAllServerList(List<T> ls) {
    
    
        if (this.serverListUpdateInProgress.compareAndSet(false, true)) {
    
    
            try {
    
    
                Iterator var2 = ls.iterator();

                while(var2.hasNext()) {
    
    
                    T s = (Server)var2.next();
                    s.setAlive(true);
                }

                this.setServersList(ls);
                super.forceQuickPing();
            } finally {
    
    
                this.serverListUpdateInProgress.set(false);
            }
        }

    }

    public String toString() {
    
    
        StringBuilder sb = new StringBuilder("DynamicServerListLoadBalancer:");
        sb.append(super.toString());
        sb.append("ServerList:" + String.valueOf(this.serverListImpl));
        return sb.toString();
    }

    public void shutdown() {
    
    
        super.shutdown();
        this.stopServerListRefreshing();
    }

    @Monitor(
        name = "LastUpdated",
        type = DataSourceType.INFORMATIONAL
    )
    public String getLastUpdate() {
    
    
        return this.serverListUpdater.getLastUpdate();
    }

    @Monitor(
        name = "DurationSinceLastUpdateMs",
        type = DataSourceType.GAUGE
    )
    public long getDurationSinceLastUpdateMs() {
    
    
        return this.serverListUpdater.getDurationSinceLastUpdateMs();
    }

    @Monitor(
        name = "NumUpdateCyclesMissed",
        type = DataSourceType.GAUGE
    )
    public int getNumberMissedCycles() {
    
    
        return this.serverListUpdater.getNumberMissedCycles();
    }

    @Monitor(
        name = "NumThreads",
        type = DataSourceType.GAUGE
    )
    public int getCoreThreads() {
    
    
        return this.serverListUpdater.getCoreThreads();
    }
}

3.5.1 IRule

 
IRule 源码:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package com.netflix.loadbalancer;

public interface IRule {
    
    

    // 该方法是根据 key 来获取 server 实例的 
    Server choose(Object var1);

    // 该方法是用来设置 ILoadBalancer 的。
    void setLoadBalancer(ILoadBalancer var1);

    // 该方法是用来获取 ILoadBalancer 的。
    ILoadBalancer getLoadBalancer();
}

 
IRule 及其实现类:

      IRule有很多默认的实现类,这些实现类根据不同的算法和逻辑来处理负载均衡的策略。
      IRule的默认实现类有以下7种。在大多数情况下,这些默认的实现类是可以满足需求的,如
果有特殊的需求,可以自己实现。IRule和其实现类之间的关系如图所示。

  • BestAvailableRule : 选择最小请求数。
  • ClientConfigEnabledRoundRobinRule : 轮 询 。
  • RandomRule : 随机选择一个 server。
  • RoundRobinRule : 轮询选择 server。
  • RetryRule : 根据轮询的方式重试。
  • WeightedResponseTimeRule : 根据响应时间去分配一个weight,weight越低,被选
    择的可能性就越低。
  • ZoneAvoidanceRule : 根据 server 的 zone 区域和可用性来轮询选择。
     
    在这里插入图片描述
     

3.5.2 定义负载均衡策略

 
1、全局策略设置


@Configuration
public class RibbonRuleConfiguration {
    
    
   
   @Bean
   public IRule ribbonRule() {
    
    
       return new RandomRule();
   }


}

2、基于注解的策略设置

如果我们想要针对某一个源服务设置其特有的策略,可以通过使用@RibbonClient 注解。

@Configuration
@AvoidScan
public class RibbonRuleConfiguration {
    
    
   
   @Autowired
   IClientConfig config
   
   @Bean
   public IRule ribbonRule(IClientConfig config) {
    
    
       return new RandomRule();
   }
}

 
       上述代码中的@AviodScan 注解是一个空的声明。注入的IClientConfig 是针对客户端的配置管理器,使用 @RibbonClient 注解时尤其需要注意它的作用。
       然后,我们需要在启动类上加@RibbonClient 注解,来对源服务进行负载约束。启动类上注解如下:
 


@RibbonClient(name = "client-a", configuration = RibbonRuleConfiguration.calss)
@ComponentScan(excludeFilters = {
    
    @ComponentScan.Filter(type = FilterType.ANNOTATION,value = {
    
    AvoidScan.class})})

 
       上面注解中 client-a 是服务名,以上注解的意思是对 client-a 服务使用的策略是经过 RibbonRuleConfiguration 所配置的。这里使用@ComponentScan 注解的意思是让 Spring 不去扫描被 @AvoidScan 注解标记的配置类,因为我们的配置是对单个源服务生效的,所以不能应用于全局,如果不排除,启动就会报错。
 
需要特别提醒的是:也可以使用@RibbonClients 注解来对多个源服务进行策略制定。 见如下代码:

@RibbonClients(value = {
    
    
    @RibbonClient(name = "client-a", configuration = RibbonRuleConfiguration.calss),
    @RibbonClient(name = "client-b", configuration = RibbonRuleConfiguration.calss),
})

 
3、基于配置文件的策略设置
 
application.yml 文件:

client-a:   # 这里是服务名
    ribbon:
        NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule

4、Ribbon 的饥饿加载

 
       Ribbon 在进行客户端负载均衡的时候,并不是在启动时就加载上下文,而在实际请求的时候才去创建,这个特性往往会让用户的第一次调用显得极度不友好,严重的时候还会引起调用超时。
 
       为了解决这个问题,我们需要通过指定 Ribbon 具体的客户端名称来开启饥饿加载,即在启动的时候,就加载所有配置项的应用程序上下文。具体配置见以下代码:

 ribbon:
     eager-load:
          enabled: true
          clients: client-a,client-b,client-c   ## 服务名

5、Ribbon 超时与重试

 
       使用 HTTP请求时,或多或少会经历网络等极端环境。此时对调用进行时限控制,以及时限之后的重试尤为重要。当前 F版本中的 Ribbon的重试机制是默认开启的,需要添加对于超时时间与重试策略的配置。具体见以下代码:
 

client-a:   # 服务名
     ribbon:
         ConnectTimeout: 3000
         ReadTimeout: 3000
         MaxAutoRetries: 1 #对第一次请求的服务重试次数
         MaxAutoRetriesNextServer: 1 # 要重试的下一次服务的最大数量(不包括第一个服务)
         OkToRetryOnAllOperations: true

6、总结

 
       Ribbon的负载均衡主要是通过 LoadBalancerClient 来实现的,而 LoadBalancerClient 具体交给了 ILoadBalancer 来处理, ILoadBalan 通过配置 IRule、IPing等,向EurekaClient 获取注册列表的信息, 默认每10秒向 EurekaClien 发送一次 "ping" ,进而检查是否需要更新服务的注册列表信息。最后, 在得到服务注册列表信息后,ILoadBalancer 根据 IRule 的策略进行负载均衡。
 
       而 RestTemplate 加上@LoadBalance注解后,在远程调度时能够负载均衡,主要是维护了一个被@LoadBalance注解的RestTemplate列表,并给该列表中的 RestTemplate 对象添加了拦截器。在拦截器的方法中, 将远程调度方法交给了Ribbon 的负载均衡器 LoadBalancerClient 去处理,从而达到了负载均衡的目的。

 
 
 
 
 
 
.

猜你喜欢

转载自blog.csdn.net/weixin_41922349/article/details/105569189