Spring Cloud与微服务构建(六)负载均衡Ribbon 6.5 源码解析 Ribbon

为了深入理解Ribbon,现在从源码的角度来讲解Ribbon,看它如何和Eureka相结合,并如何和RestTemplate相结合来做负载均衡。首先,跟踪LoadBalancerClient的源码,它是一个接口类,继承了ServiceInstanceChooser,它的实现类为RibbonLoadBalanceClient。

LoadBalancerClient是一个负载均衡的客户端,有如下3种方法。其中有2个excute()方法,均用来执行请求,reconstructURI()用于重构Url,代码如下:

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);
}

ServiceInstanceChooser接口有一个方法用于根据serviceId获取ServiceInstance,即通过服务名来选择服务实例,代码如下:

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

LoadBalancerClient的实现类为RibbonLoadBalancerClient。RibbonLoadBalancerClient是一个非常重要的类,最终的负载均衡的请求处理由它来执行。RibbonLoadBalancerClient的部分源码如下:

//
// 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 {
    private SpringClientFactory clientFactory;

    public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {
        this.clientFactory = clientFactory;
    }

    public URI reconstructURI(ServiceInstance instance, URI original) {
        Assert.notNull(instance, "instance can not be null");
        String serviceId = instance.getServiceId();
        RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
        Server server = new Server(instance.getHost(), instance.getPort());
        IClientConfig clientConfig = this.clientFactory.getClientConfig(serviceId);
        ServerIntrospector serverIntrospector = this.serverIntrospector(serviceId);
        URI uri = RibbonUtils.updateToHttpsIfNeeded(original, clientConfig, serverIntrospector, server);
        return context.reconstructURIWithServer(server, uri);
    }

    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));
    }

    public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
        ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
        Server server = this.getServer(loadBalancer);
        if (server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        } else {
            RibbonLoadBalancerClient.RibbonServer ribbonServer = new RibbonLoadBalancerClient.RibbonServer(serviceId, server, this.isSecure(server, serviceId), this.serverIntrospector(serviceId).getMetadata(server));
            return this.execute(serviceId, ribbonServer, request);
        }
    }

    public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {
        Server server = null;
        if (serviceInstance instanceof RibbonLoadBalancerClient.RibbonServer) {
            server = ((RibbonLoadBalancerClient.RibbonServer)serviceInstance).getServer();
        }

        if (server == null) {
            throw new IllegalStateException("No instances available for " + serviceId);
        } else {
            RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
            RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

            try {
                T returnVal = request.apply(serviceInstance);
                statsRecorder.recordStats(returnVal);
                return returnVal;
            } catch (IOException var8) {
                statsRecorder.recordStats(var8);
                throw var8;
            } catch (Exception var9) {
                statsRecorder.recordStats(var9);
                ReflectionUtils.rethrowRuntimeException(var9);
                return null;
            }
        }
    }

    private ServerIntrospector serverIntrospector(String serviceId) {
        ServerIntrospector serverIntrospector = (ServerIntrospector)this.clientFactory.getInstance(serviceId, ServerIntrospector.class);
        if (serverIntrospector == null) {
            serverIntrospector = new DefaultServerIntrospector();
        }

        return (ServerIntrospector)serverIntrospector;
    }

    private boolean isSecure(Server server, String serviceId) {
        IClientConfig config = this.clientFactory.getClientConfig(serviceId);
        ServerIntrospector serverIntrospector = this.serverIntrospector(serviceId);
        return RibbonUtils.isSecure(config, serverIntrospector, 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);
    }

    public static class RibbonServer implements ServiceInstance {
        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;
        }

        public String getServiceId() {
            return this.serviceId;
        }

        public String getHost() {
            return this.server.getHost();
        }

        public int getPort() {
            return this.server.getPort();
        }

        public boolean isSecure() {
            return this.secure;
        }

        public URI getUri() {
            return DefaultServiceInstance.getUri(this);
        }

        public Map<String, String> getMetadata() {
            return this.metadata;
        }

        public Server getServer() {
            return this.server;
        }

        public String toString() {
            StringBuffer sb = new StringBuffer("RibbonServer{");
            sb.append("serviceId='").append(this.serviceId).append('\'');
            sb.append(", server=").append(this.server);
            sb.append(", secure=").append(this.secure);
            sb.append(", metadata=").append(this.metadata);
            sb.append('}');
            return sb.toString();
        }
    }
}

在RibbonLoadBalancerClient的源码中,choose()方法用于选择具体服务实例。该方法通过getServer()方法去获取实例,经过源码跟踪,最终交给ILoadBalancer类去选择服务实例。ILoadBalancer在ribbon-loadbalancer的jar包下,ILoadBalancer是一个接口,该接口定义了一系列实现负载均衡的方法,源码如下

public interface ILoadBalancer {
    void addServers(List<Server> var1);

    Server chooseServer(Object var1);

    void markServerDown(Server var1);

    /** @deprecated */
    @Deprecated
    List<Server> getServerList(boolean var1);

    List<Server> getReachableServers();

    List<Server> getAllServers();
}

其中,addServers()方法用于添加一个Server集合,chooseServer()方法用于根据key去获取Server,markSererDown()方法用于标记某个服务下线,getReachableServers()获取可用的Server集合,getAllServers()获取所有的Server集合。

IloadBalancer的子类为BaseLoadBalancer的实现类为DynamicServer-ListLoadBalancer。
查看DynamicServerListLoadBalancer类的源码,DynamicServerListLoadBalancer需要配置IClientConfig、IRule、IPing、ServerList、ServerListFilter和ILoadBalancer。查看BaseLoadBalancer类的源码,在默认的情况下,实现了如下配置。

  • IClienConfig ribbonClientConfig:DefaultClientConfigImpl。
  • IRule ribbonRule:RoundRobinRule
  • IPing ribbonPing:DummyPing
  • ServerList ribbonServerList:ConfigurationBasedServerList。
  • ServerListFilter ribbonServerListFilter:ZonePreferenceServerListFilter。
  • ILoadBalance ribbonLoadBalacer:ZoneAwareLoadBalancer。

IClientConfig用于配置负载均衡的客户端,IClientConfig的默认实现类为DefaultClientConfigImpl。
IRule用于配置负载均衡的策略,IRule有3个方法,其中choose()是根据key来获取server实例的,setLoadBalancer()和getLoadBalancer()是用来设置和获取ILoadBalancer的,它的源码如下:

public interface IRule {
    Server choose(Object var1);

    void setLoadBalancer(ILoadBalancer var1);

    ILoadBalancer getLoadBalancer();
}

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

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

IPing用于向server发送“ping”,来判断该server是否有响应,从而判断该server是否可用。它有一个isAlive()方法,源码如下:

public interface IPing {
    boolean isAlive(Server var1);
}

IPing的实现类有PingUrl、PingConstant、NoOpPing、DummyPing和NIWSDiscoveryPing。

  • PingUrl:真实地去ping某个Url,判断其是否可用。
  • PingConstant:固定返回某个服务是否可用,默认返回true,即可用。
  • NoOpPing:不去ping,直接返回true,即可用。
  • DummyPing:直接返回true,并实现了initWithNiwsConfig方法。
  • NIWSDiscoveryPing:根据DiscoveryEnabledServer的InstanceInfo的InstanceStatus去判断,如果为InstanceStatus.UP,则可用,否则不可用。

ServerList是定义获取所有server的注册列表信息的接口,它的代码如下:

public interface ServerList<T extends Server> {
    List<T> getInitialListOfServers();

    List<T> getUpdatedListOfServers();
}

ServerListFilter接口定义了可根据配置去过滤或者特性动态地获取符合条件的server列表的方法,代码如下:

public interface ServerListFilter<T extends Server> {
    List<T> getFilteredListOfServers(List<T> var1);
}

阅读DynamicServerListLoadBalancer的源码,DynamicServerListLoadBalancer的构造函数中有一个initWithNiwsConfig()方法。在该方法中经过一系列的初始化配置,最终执行了restOfInit()方法。DynamicServerListLoadBalancer的部分源码如下:

    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);
        }
    }

在restOfInit()方法中,有一个updateListOfServers()的方法,该方法是用来获取所有的ServerList的。

    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());
    }

进一步跟踪updateListOfServers()方法的源码,最终由serverListImpl.getUpdatedListOfServers()获取所有的服务列表,代码如下:

    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);
    }

而serverListImpl是ServerList接口的具体实现类。跟踪源码,ServerList的实现类为DiscoveryEnabledNIWSServerList,这个类在ribbon-eureka.jar的com.netflix.niws.loadbalancer包下。
其中,DiscoveryEnabledNIWSServerList有getInitialListOfServers()和getUpdatedListOfServers()方法,具体代码如下:

    public List<DiscoveryEnabledServer> getInitialListOfServers() {
        return this.obtainServersViaDiscovery();
    }

    public List<DiscoveryEnabledServer> getUpdatedListOfServers() {
        return this.obtainServersViaDiscovery();
    }

继续跟踪源码,obtainServersViaDiscovery()方法是根据eurekaClientProvider.get()方法来获取EurekaClient的,再根据EurekaClient来获取服务注册列表信息,代码如下:

    private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {
        List<DiscoveryEnabledServer> serverList = new ArrayList();
        if (this.eurekaClientProvider != null && this.eurekaClientProvider.get() != null) {
            EurekaClient eurekaClient = (EurekaClient)this.eurekaClientProvider.get();
            if (this.vipAddresses != null) {
                String[] var3 = this.vipAddresses.split(",");
                int var4 = var3.length;

                for(int var5 = 0; var5 < var4; ++var5) {
                    String vipAddress = var3[var5];
                    List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, this.isSecure, this.targetRegion);
                    Iterator var8 = listOfInstanceInfo.iterator();

                    while(var8.hasNext()) {
                        InstanceInfo ii = (InstanceInfo)var8.next();
                        if (ii.getStatus().equals(InstanceStatus.UP)) {
                            if (this.shouldUseOverridePort) {
                                if (logger.isDebugEnabled()) {
                                    logger.debug("Overriding port on client name: " + this.clientName + " to " + this.overridePort);
                                }

                                InstanceInfo copy = new InstanceInfo(ii);
                                if (this.isSecure) {
                                    ii = (new Builder(copy)).setSecurePort(this.overridePort).build();
                                } else {
                                    ii = (new Builder(copy)).setPort(this.overridePort).build();
                                }
                            }

                            DiscoveryEnabledServer des = new DiscoveryEnabledServer(ii, this.isSecure, this.shouldUseIpAddr);
                            des.setZone(DiscoveryClient.getZone(ii));
                            serverList.add(des);
                        }
                    }

                    if (serverList.size() > 0 && this.prioritizeVipAddressBasedServers) {
                        break;
                    }
                }
            }

            return serverList;
        } else {
            logger.warn("EurekaClient has not been initialized yet, returning an empty list");
            return new ArrayList();
        }
    }

其中,eurekaClientProvider的实现类是LegacyEurekaClientProvider,LegacyEurekaClientProvider是一个获取eurekaClient实例的类,其代码如下:

class LegacyEurekaClientProvider implements Provider<EurekaClient> {
    private volatile EurekaClient eurekaClient;

    LegacyEurekaClientProvider() {
    }

    public synchronized EurekaClient get() {
        if (this.eurekaClient == null) {
            this.eurekaClient = DiscoveryManager.getInstance().getDiscoveryClient();
        }

        return this.eurekaClient;
    }
}

EurekaClient的实现类为DiscoveryClient,在上一章已经分析了。DiscoveryClient具有服务注册、获取服务注册列表等功能。

由此可见,负载均衡器是从Eureka Client获取服务列表信息的,并根据IRule的策略去路由,根据IPing去判断服务的可用性。

那么现在还有一个问题,负载均衡器每隔多长时间从Eureka Client获取注册信息呢?

在BaseLoadBalancer类的源码中,在BaseLoadBalancer的构造方法开启了一个PingTask任务,代码如下:

   public BaseLoadBalancer(String name, IRule rule, LoadBalancerStats stats, IPing ping, IPingStrategy pingStrategy) {
        this.rule = DEFAULT_RULE;
        this.pingStrategy = DEFAULT_PING_STRATEGY;
        this.ping = null;
        this.allServerList = Collections.synchronizedList(new ArrayList());
        this.upServerList = Collections.synchronizedList(new ArrayList());
        this.allServerLock = new ReentrantReadWriteLock();
        this.upServerLock = new ReentrantReadWriteLock();
        this.name = "default";
        this.lbTimer = null;
        this.pingIntervalSeconds = 10;
        this.maxTotalPingTimeSeconds = 5;
        this.serverComparator = new ServerComparator();
        this.pingInProgress = new AtomicBoolean(false);
        this.counter = Monitors.newCounter("LoadBalancer_ChooseServer");
        this.enablePrimingConnections = false;
        this.changeListeners = new CopyOnWriteArrayList();
        this.serverStatusListeners = new CopyOnWriteArrayList();
        logger.debug("LoadBalancer [{}]:  initialized", name);
        this.name = name;
        this.ping = ping;
        this.pingStrategy = pingStrategy;
        this.setRule(rule);
        this.setupPingTask();
        this.lbStats = stats;
        this.init();
    }

在setupPingTask()的具体代码逻辑里,开启了ShutdownEnabledTimer的PingTask任务,在默认情况下,变量pingIntervalSeconds的值为10,即每10秒向EurekaClient发送一次心跳“ping”。

    void setupPingTask() {
        if (!this.canSkipPing()) {
            if (this.lbTimer != null) {
                this.lbTimer.cancel();
            }

            this.lbTimer = new ShutdownEnabledTimer("NFLoadBalancer-PingTimer-" + this.name, true);
            this.lbTimer.schedule(new BaseLoadBalancer.PingTask(), 0L, (long)(this.pingIntervalSeconds * 1000));
            this.forceQuickPing();
        }
    }

查看PingTask的源码,PingTask创建了一个Pinger对象,并执行了runPinger()方法。

    class PingTask extends TimerTask {
        PingTask() {
        }

        public void run() {
            try {
                (BaseLoadBalancer.this.new Pinger(BaseLoadBalancer.this.pingStrategy)).runPinger();
            } catch (Exception var2) {
                BaseLoadBalancer.logger.error("LoadBalancer [{}]: Error pinging", BaseLoadBalancer.this.name, var2);
            }

        }
    }

查看Pinger的runPinger()方法,最终根据pingerStrategy.pingServers(ping,allServers)来获取服务的可用性,如果该返回结果与之前相同,则不向EurekaClient获取注册列表;如果不同,则通知ServerStatusChangeListener服务注册列表信息发生了改变,进行更新或者重新摘取,代码如下:

        public void runPinger() throws Exception {
            if (BaseLoadBalancer.this.pingInProgress.compareAndSet(false, true)) {
                Server[] allServers = null;
                boolean[] results = null;
                Lock allLock = null;
                Lock upLock = null;

                try {
                    allLock = BaseLoadBalancer.this.allServerLock.readLock();
                    allLock.lock();
                    allServers = (Server[])BaseLoadBalancer.this.allServerList.toArray(new Server[BaseLoadBalancer.this.allServerList.size()]);
                    allLock.unlock();
                    int numCandidates = allServers.length;
                    boolean[] resultsx = this.pingerStrategy.pingServers(BaseLoadBalancer.this.ping, allServers);
                    List<Server> newUpList = new ArrayList();
                    List<Server> changedServers = new ArrayList();

                    for(int i = 0; i < numCandidates; ++i) {
                        boolean isAlive = resultsx[i];
                        Server svr = allServers[i];
                        boolean oldIsAlive = svr.isAlive();
                        svr.setAlive(isAlive);
                        if (oldIsAlive != isAlive) {
                            changedServers.add(svr);
                            BaseLoadBalancer.logger.debug("LoadBalancer [{}]:  Server [{}] status changed to {}", new Object[]{BaseLoadBalancer.this.name, svr.getId(), isAlive ? "ALIVE" : "DEAD"});
                        }

                        if (isAlive) {
                            newUpList.add(svr);
                        }
                    }

                    upLock = BaseLoadBalancer.this.upServerLock.writeLock();
                    upLock.lock();
                    BaseLoadBalancer.this.upServerList = newUpList;
                    upLock.unlock();
                    BaseLoadBalancer.this.notifyServerStatusChangeListener(changedServers);
                } finally {
                    BaseLoadBalancer.this.pingInProgress.set(false);
                }
           }

由此可见,LoadBalancerClient是在初始化时向Eureka获取服务注册列表信息,并且每10秒向EurekaClient发送“ping”,来判断服务的可用性。如果服务的可用性发生了改变或者服务数量和之前的不一致,则更新或者重新拉取。LoadBalancerClient有了这些服务注册列表信息,就可以根据具体的IRule的策略来进行负载均衡。

最后,回到问题的本身,为什么在RestTemplate类的Bean上加一个@LoadBalance注解就可以使用Ribbon的负载均衡呢?

全局搜索(IDEA的快捷键为“Ctrl”+“Shift”+“F”)查看有哪些类用到了@LoadBalanced注解。通过搜索,可以发现LoadBalancerAUtoConfiguration类(LoadBalacer自动配置类)使用到了该注解,LoadBalancerAutoConfiguration类的代码如下:

@Configuration
@ConditionalOnClass({RestTemplate.class})
@ConditionalOnBean({LoadBalancerClient.class})
@EnableConfigurationProperties({LoadBalancerRetryProperties.class})
public class LoadBalancerAutoConfiguration {
    @LoadBalanced
    @Autowired(
        required = false
    )
    private List<RestTemplate> restTemplates = Collections.emptyList();
    @Autowired(
        required = false
    )
    private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

    public LoadBalancerAutoConfiguration() {
    }

    @Bean
    public SmartInitializingSingleton loadBalancedRestTemplateInitializer(final List<RestTemplateCustomizer> customizers) {
        return new SmartInitializingSingleton() {
            public void afterSingletonsInstantiated() {
                Iterator var1 = LoadBalancerAutoConfiguration.this.restTemplates.iterator();

                while(var1.hasNext()) {
                    RestTemplate restTemplate = (RestTemplate)var1.next();
                    Iterator var3 = customizers.iterator();

                    while(var3.hasNext()) {
                        RestTemplateCustomizer customizer = (RestTemplateCustomizer)var3.next();
                        customizer.customize(restTemplate);
                    }
                }

            }
        };
    }

    @Bean
    @ConditionalOnMissingBean
    public LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {
        return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
    }

    @Configuration
    @ConditionalOnClass({RetryTemplate.class})
    static class RetryAutoConfiguration {
        RetryAutoConfiguration() {
        }

        @Bean
        public RetryTemplate retryTemplate() {
            RetryTemplate template = new RetryTemplate();
            template.setThrowLastExceptionOnExhausted(true);
            return template;
        }

        @Bean
        @ConditionalOnMissingBean
        public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory() {
            return new NeverRetryFactory();
        }

        @Bean
        public RetryLoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties, LoadBalancedRetryPolicyFactory lbRetryPolicyFactory, LoadBalancerRequestFactory requestFactory) {
            return new RetryLoadBalancerInterceptor(loadBalancerClient, this.retryTemplate(), properties, lbRetryPolicyFactory, requestFactory);
        }

        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
            return new RestTemplateCustomizer() {
                public void customize(RestTemplate restTemplate) {
                    List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
                    list.add(loadBalancerInterceptor);
                    restTemplate.setInterceptors(list);
                }
            };
        }
    }

    @Configuration
    @ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
    static class LoadBalancerInterceptorConfig {
        LoadBalancerInterceptorConfig() {
        }

        @Bean
        public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
            return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
        }

        @Bean
        @ConditionalOnMissingBean
        public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
            return new RestTemplateCustomizer() {
                public void customize(RestTemplate restTemplate) {
                    List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
                    list.add(loadBalancerInterceptor);
                    restTemplate.setInterceptors(list);
                }
            };
        }
    }
}

在LoadBalancerAutoConfiguration类中,首先维护了一个被@LoadBalanced修饰的RestTemplate对象的List。在初始化的过程中,通过调用customizer.customize(restTemplate)方法来给RestTemplate增加拦截器LoadBalancerInterceptor。LoadBalancerInterceptor用于实时拦截,在LoadBalancerInterceptor中实现了负载均衡的方法。LoadBalancerInterceptor类的拦截方法的代码如下:

    public ListenableFuture<ClientHttpResponse> intercept(final HttpRequest request, final byte[] body, final AsyncClientHttpRequestExecution execution) throws IOException {
        URI originalUri = request.getURI();
        String serviceName = originalUri.getHost();
        return (ListenableFuture)this.loadBalancer.execute(serviceName, new LoadBalancerRequest<ListenableFuture<ClientHttpResponse>>() {
            public ListenableFuture<ClientHttpResponse> apply(ServiceInstance instance) throws Exception {
                HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, AsyncLoadBalancerInterceptor.this.loadBalancer);
                return execution.executeAsync(serviceRequest, body);
            }
        });
    }

综上所述,Ribbon的负载均衡主要是通过LoadBalancerClient来实现的,而LoadBalancerClient具体交给了ILoadBalancer来处理,ILoadBalancer通过配置IRule、IPing等,向EurekaClient获取注册列表的信息,默认每10秒向EurekaClient发送一次“ping”,进而检查是否需要更新服务的注册列表信息。最后,在得到服务注册列表信息后,ILoadBalancer根据IRule的策略进行负载均衡。

而RestTemplate加上@LoadBalance注解后,在远程调度时能够负载均衡,主要是维护了一个被@LoadBalance注解的RestTemplate列表,并给该列表中的RestTemplate对象添加了拦截器。在拦截器的方法中,将远程调试方法交给了Ribbon的负载均衡器LoadBalancerClient去处理,从而达到了负载均衡的目的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值