微服务开发(11)--Ribbon&Feign源码解析

Ribbon源码解析

Ribbon的使用流程图

在这里插入图片描述

  1. 从上图我们可以看到我们首先创建了一个Eureka集群,端口号分别为8001和8002
  2. User1服务和User2服务作为服务的提供方将自己的服务信息注册到Eureka中
  3. 此时Order服务作为服务的消费者,我们会首先从Eureka中拉取服务注册列表,放到本地,做为缓存
  4. ribbon拉取的服务注册列表则是Order本都服务的注册列表,而不是自己从Eureka中拉取的服务
  5. 我们在Order服务中使用resrTemplate.getForObject等方法调用,在调用之前会经过一个拦截器,该拦截器会将我们的参数中服务名字替换为具体的IP+端口的形式(拦截器是LoadBalancerInterceptor,RibbonLoadBalancerClient会将user-service替换为192.168.0.1:8002)
  6. 我们得到真实服务的http连接和端口,用此URL进行真实的调动。
  7. 注意Ribbon有一个Ping机制,该机制的作用是:如果通过ribbon多次调用服务的提供方,多次调用失败之后,会自动去除Ribbon的服务列表,那么再次访问的时候,就不会在访问这个失败的url
Ribbon的源码简介

在这里插入图片描述

  1. 我们在使用RestTemplate的时候,会使用@LoadBalance注解标识,我们找到@LoadBalance的注解所在的包,然后去同包目录下面找XXXXConfiguration类。因为这种类,我们会由SpringBoot自动进行配置。
    在这里插入图片描述
  2. 我们发现在@LoadBalance注解的同包下面类LoadBalancerAutoConfiguration类,我们点进去观察
/*
 * Copyright 2013-2017 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.client.loadbalancer;

import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.SmartInitializingSingleton;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.client.ClientHttpRequestInterceptor;
import org.springframework.retry.backoff.BackOffPolicy;
import org.springframework.retry.support.RetryTemplate;
import org.springframework.web.client.RestTemplate;

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

/**
 * Auto configuration for Ribbon (client side load balancing).
 *
 * @author Spencer Gibb
 * @author Dave Syer
 * @author Will Tran
 * @author Gang Li
 */
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();

	@Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
			final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
            for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
                for (RestTemplateCustomizer customizer : customizers) {
                    customizer.customize(restTemplate);
                }
            }
        });
	}

	@Autowired(required = false)
	private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

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

	@Configuration
	@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
	static class LoadBalancerInterceptorConfig {
		@Bean
		public LoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}

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

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

		@Bean
		@ConditionalOnMissingBean
		public LoadBalancedRetryFactory loadBalancedRetryFactory() {
			return new LoadBalancedRetryFactory() {};
		}
	}

	@Configuration
	@ConditionalOnClass(RetryTemplate.class)
	public static class RetryInterceptorAutoConfiguration {
		@Bean
		@ConditionalOnMissingBean
		public RetryLoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties,
				LoadBalancerRequestFactory requestFactory,
				LoadBalancedRetryFactory loadBalancedRetryFactory) {
			return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
					requestFactory, loadBalancedRetryFactory);
		}

		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                list.add(loadBalancerInterceptor);
                restTemplate.setInterceptors(list);
            };
		}
	}
}
  1. LoadBalancerAutoConfiguration类下面主要是初始化了一堆bean,这里挑几个重要的看

(3.1)LoadBalancerInterceptor

  • 该类的作用就是创建一个LoadBalancerInterceptor对象
		@Bean
		public LoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}

(3.2)RestTemplateCustomizer

  • 该类的作用是返回一个RestTemplateCustomizer类型的定制器
  • 我们将给所有的RestTemplate中加入一个LoadBalancerInterceptor拦截器(该拦截器就是我们上面初始化的那个拦截器)
  • 会在后面的SmartInitializingSingleton中调用执行这个方法。
		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			// lambda表达式
			return restTemplate -> {
				// 遍历所有的restTemplate,将RestTemplate的所有拦截器放到list中
                List<ClientHttpRequestInterceptor> list = new ArrayList<>(
                        restTemplate.getInterceptors());
                // 将该list加上我们最新的刚刚创建的LoadBalancerInterceptor拦截器
                list.add(loadBalancerInterceptor);
                // 将其设置到restTemplate中
                restTemplate.setInterceptors(list);
            };
		}

(3.3)SmartInitializingSingleton

  • 该方法的作用执行所有的定制器,遍历RestTemplates列表(这个列表是在LoadBalancerAutoConfiguration类中声明的,下面会对其进行介绍),该定制器的作用其实就是给每个RestTemplate中加入拦截器,这里就是我们刚刚创建的RestTemplateCustomizer的bean对象的执行。刚才的是声明RestTemplateCustomizer类。
	@Bean
	public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
			final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
		// 刚刚的RestTemplateCustomizer restTemplateCustomizer方法的定制器当做参数被传递进来,作为定制器使用(这里的customizers其中一个就是restTemplateCustomizer定制器)
		// 便利了所有的标有@LoadBalance注解的restTemplate
		// 然后用该定制器定制这些restTemplate
		// 该定制器就是给restTemplate里面加一个拦截器
		return () -> restTemplateCustomizers.ifAvailable(customizers -> {
		// 定制器的回调,我们将所有的定制器定制到restTemplate中
		// 里面的LoadBalancerAutoConfiguration.this.restTemplates就是我们上面这个类定义的RestTemplate,上面注有@LoadBalance注解
			for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
			// 定制我们的RestTemplate
			// 实际上调用的就是我们的RestTemplateCustomizer restTemplateCustomizer方法
			// 给每一个RestTemplate加一个拦截器
				for (RestTemplateCustomizer customizer : customizers) {
					customizer.customize(restTemplate);
				}
			}
		});
	}

(3.4)private List restTemplates = Collections.emptyList();
在这里插入图片描述

  • 这就是我们在SmartInitializingSingleton方法中的参数restTemplates
  • 容器在启动的时候会扫描所有的带有@LoadBalance注解的restTemplate,将所有的restTemplate放到这个集合中。
  1. 我们回到新建LoadBalancerInterceptor的方法
    在这里插入图片描述
  • 这里我们可以看到其方法中有一个形参LoadBalancerClient loadBalancerClient,也就是说我们的LoadBalancerClient也是注入进来的,那么这个对象又是在哪里初始化的?
  • LoadBalancerClient是一个接口,表示的是一个客户端的负载均衡器,所有的操作都是在这个对象中完成的。

(4.1)RibbonLoadBalancerClient

  • 该类实现了LoadBalancerClient
    在这里插入图片描述
  • 我们找到该类同包下的XXXXAutoConfiguration,XXXConfiguration类由SpringBoot自动加载
    在这里插入图片描述
    (4.2)RibbonAutoConfiguration
    在这里插入图片描述
  • 看上面的截图我们可以看到这里面有一个@bean自动创建了LoadBalancerClient对象
  • 再看类上面的注解@AutoConfigureBefore,@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
    表示的是在加载LoadBalancerAutoConfiguration类之前,我们会先加载这个类,这个类中又有初始化LoadBalancerClient对象,所以我们在调用LoadBalancerAutoConfiguration的LoadBalancerInterceptor方法的时候,一切都说的请了
    在这里插入图片描述
  1. 上面的代码我们已经分析的告一段落,现在回到我们的调用
  • 我们平常的调用是用restTemplate进行调用的,若我们在调用的URL中写的是服务的名字,而不是具体的IP+端口号,则一定要在声明RestTemplate的上面加上@LoadBalance
/**
 * 配置类,声明RestTemplate
 * @author 92823
 */
@Configuration
public class MyConfig {

    @Bean
    @LoadBalanced
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}
--------
/**
 * @author 92823
 */
@RestController
public class UserController {

    @Autowired
    private RestTemplate restTemplate;

    @GetMapping("/queryUerInfoById/{id}")
    public String queryUerInfoById(@PathVariable String id) {
        ResponseEntity<String> forEntity = restTemplate.getForEntity("http://my-eureka-consumer-order-1/queryOrderById/" + id, String.class);
        String body = forEntity.getBody();
        return body;
    }

}
  1. 我们在利用RestTemplate调用之前,会被拦截器进行拦截
  2. 该拦截器是LoadBalancerInterceptor.intercept()方法进行拦截
    在这里插入图片描述
  • 其中最主要的方法就是intercept中的execute方法

(7.1)LoadBalancerClient.execute()

  • LoadBalancrtClient是一个抽象类,这里利用多太实现,其具体的实现类上面分析了,应该是RibbonLoadBalancerClient进行实现
	@Override
	public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
		// 1. 根据serviceId获取一个负载均衡器
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
		// 2. 利用当前的负载均衡器,依据其负载均衡算法选择一个server
		Server server = getServer(loadBalancer);
		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);
	}
  • ILoadBalancer loadBalancer = getLoadBalancer(serviceId);表示的是根据serviceId获取一个负载均衡器
  • Server server = getServer(loadBalancer);表示的是利用当前的负载均衡器,依据其负载均衡算法,选择一个server

(7.1.1)getLoadBalancer

	protected ILoadBalancer getLoadBalancer(String serviceId) {
		return this.clientFactory.getLoadBalancer(serviceId);
	}
  • 我们的loadBalancer是在容器中获取到的,那么我们猜测一下,这里肯定是在某一个地方进行@Bean初始化的
  • 一般情况下我们是要找到某个类初始化的地方,可以在使用该类对象的类统计目录找到相关的Configuration类里面
    在这里插入图片描述
  • 初始化ZoneAwareLoadBalancer
	@Bean
	@ConditionalOnMissingBean
	public ILoadBalancer ribbonLoadBalancer(IClientConfig config,
			ServerList<Server> serverList, ServerListFilter<Server> serverListFilter,
			IRule rule, IPing ping, ServerListUpdater serverListUpdater) {
		if (this.propertiesFactory.isSet(ILoadBalancer.class, name)) {
			return this.propertiesFactory.get(ILoadBalancer.class, config, name);
		}
		return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
				serverListFilter, serverListUpdater);
	}
  • 其中的ServerList是来自:
	@Bean
	@ConditionalOnMissingBean
	@SuppressWarnings("unchecked")
	public ServerList<Server> ribbonServerList(IClientConfig config) {
		if (this.propertiesFactory.isSet(ServerList.class, name)) {
			return this.propertiesFactory.get(ServerList.class, config, name);
		}
		ConfigurationBasedServerList serverList = new ConfigurationBasedServerList();
		serverList.initWithNiwsConfig(config);
		return serverList;
	}
  • 所以我们知道了ILoadBalancer loadBalancer = getLoadBalancer(serviceId);默认拿到的是ZoneAwareLoadBalancer

(7.1.2)Server server = getServer(loadBalancer);

  • 该方法的主要作用就是根据传进来的负载均衡器,依据指定算法挑选一个Server
	protected Server getServer(ILoadBalancer loadBalancer) {
		if (loadBalancer == null) {
			return null;
		}
		return loadBalancer.chooseServer("default"); // TODO: better handling of key
	}
	------------------// LoadBalancer是ZoneAwareLoadBalancer实现的,所以具体的choose访问的是ZoneAwareLoadBalancer类中的
    @Override
    public Server chooseServer(Object key) {
        if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
            logger.debug("Zone aware logic disabled or there is only one zone");
            // 默认我们走的就是这里
            return super.chooseServer(key);
        }
        Server server = null;
        try {
            LoadBalancerStats lbStats = getLoadBalancerStats();
            Map<String, ZoneSnapshot> zoneSnapshot = ZoneAvoidanceRule.createSnapshot(lbStats);
            logger.debug("Zone snapshots: {}", zoneSnapshot);
            if (triggeringLoad == null) {
                triggeringLoad = DynamicPropertyFactory.getInstance().getDoubleProperty(
                        "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".triggeringLoadPerServerThreshold", 0.2d);
            }

            if (triggeringBlackoutPercentage == null) {
                triggeringBlackoutPercentage = DynamicPropertyFactory.getInstance().getDoubleProperty(
                        "ZoneAwareNIWSDiscoveryLoadBalancer." + this.getName() + ".avoidZoneWithBlackoutPercetage", 0.99999d);
            }
            Set<String> availableZones = ZoneAvoidanceRule.getAvailableZones(zoneSnapshot, triggeringLoad.get(), triggeringBlackoutPercentage.get());
            logger.debug("Available zones: {}", availableZones);
            if (availableZones != null &&  availableZones.size() < zoneSnapshot.keySet().size()) {
                String zone = ZoneAvoidanceRule.randomChooseZone(zoneSnapshot, availableZones);
                logger.debug("Zone chosen: {}", zone);
                if (zone != null) {
                    BaseLoadBalancer zoneLoadBalancer = getLoadBalancer(zone);
                    server = zoneLoadBalancer.chooseServer(key);
                }
            }
        } catch (Exception e) {
            logger.error("Error choosing server using zone aware logic for load balancer={}", name, e);
        }
        if (server != null) {
            return server;
        } else {
            logger.debug("Zone avoidance logic is not invoked.");
            return super.chooseServer(key);
        }
    }
  • 默认我们走的就是return super.chooseServer(key);这个方法
  • BaseLoadBalancer.chooseServer
    public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
            	// 默认选择的就是轮训算法
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }
  • return rule.choose(key);我们默认选择的就是轮训算法:
    在这里插入图片描述
  • RoundRobinRule的choose方法
    public Server choose(ILoadBalancer lb, Object key) {
        if (lb == null) {
            log.warn("no load balancer");
            return null;
        }

        Server server = null;
        int count = 0;
        while (server == null && count++ < 10) {
            List<Server> reachableServers = lb.getReachableServers();
            // 得到所有的服务列表
            List<Server> allServers = lb.getAllServers();
            int upCount = reachableServers.size();
            int serverCount = allServers.size();

            if ((upCount == 0) || (serverCount == 0)) {
                log.warn("No up servers available from load balancer: " + lb);
                return null;
            }

            int nextServerIndex = incrementAndGetModulo(serverCount);
            server = allServers.get(nextServerIndex);

            if (server == null) {
                /* Transient. */
                Thread.yield();
                continue;
            }

            if (server.isAlive() && (server.isReadyToServe())) {
                return (server);
            }

            // Next.
            server = null;
        }

        if (count >= 10) {
            log.warn("No available alive servers after 10 tries from load balancer: "
                    + lb);
        }
        return server;
    }
  1. 我们最终的两个列表就是allServerListupServerList
    @Monitor(name = PREFIX + "AllServerList", type = DataSourceType.INFORMATIONAL)
    protected volatile List<Server> allServerList = Collections
            .synchronizedList(new ArrayList<Server>());
    @Monitor(name = PREFIX + "UpServerList", type = DataSourceType.INFORMATIONAL)
    protected volatile List<Server> upServerList = Collections
            .synchronizedList(new ArrayList<Server>());

(8.1)RibbonClientConfiguration

  • 这里我们继续看下这个类
    在这里插入图片描述
  • 我们在这个类中实现了PollingServerListUpdater将其作为bean注入到容器中,并且在IloadBalancer对象的生成的时候,将其作为参数传递
    在这里插入图片描述
  • 我们的PollingServerListUpdater实现了ServerListUpdater。

(8.2)DynamicServerListLoadBalancer

  • 该类继承了BaseLoadBalancer,初始化ZoneAwareLoadBalancer的时候,实际上是初始化DynamicServerListLoadBalancer类
    public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule,
                                 IPing ping, ServerList<T> serverList, ServerListFilter<T> filter,
                                 ServerListUpdater serverListUpdater) {
        // 初始化的实际上就是DynamicServerListLoadBalancer
        super(clientConfig, rule, ping, serverList, filter, serverListUpdater);
    }
  • 进入其构造方法
    在这里插入图片描述
  • 这里最重要的就是restOfInit(clientConfig);方法

(8.3)restOfInit(clientConfig);

    void restOfInit(IClientConfig clientConfig) {
        boolean primeConnection = this.isEnablePrimingConnections();
        // turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
        this.setEnablePrimingConnections(false);
        // ribbon定时更新eureka实例列表
        enableAndInitLearnNewServersFeature();
        // 获取所有的eureka实例列表
        updateListOfServers();
        if (primeConnection && this.getPrimeConnections() != null) {
            this.getPrimeConnections()
                    .primeConnections(getReachableServers());
        }
        this.setEnablePrimingConnections(primeConnection);
        LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
    }

(8.4)enableAndInitLearnNewServersFeature();

  • 该方法的作用是定时更新Eureka的实例列表
    /**
     * Feature that lets us add new instances (from AMIs) to the list of
     * existing servers that the LB will use Call this method if you want this
     * feature enabled
     */
    public void enableAndInitLearnNewServersFeature() {
        LOGGER.info("Using serverListUpdater {}", serverListUpdater.getClass().getSimpleName());
        serverListUpdater.start(updateAction);
    }

-------这个start方法走的是PollingServerListUpdater的

    @Override
    public synchronized void start(final UpdateAction updateAction) {
        if (isActive.compareAndSet(false, true)) {
            final Runnable wrapperRunnable = new Runnable() {
            	// 新建一个线程去执行
                @Override
                public void run() {
                    if (!isActive.get()) {
                        if (scheduledFuture != null) {
                            scheduledFuture.cancel(true);
                        }
                        return;
                    }
                    try {
                    	// 更新列表,其实调用的是DynamicServerListLoadBalancer.this.updateListOfServers();
                        updateAction.doUpdate();
                        lastUpdated = System.currentTimeMillis();
                    } catch (Exception e) {
                        logger.warn("Failed one update cycle", e);
                    }
                }
            };

            scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
                    wrapperRunnable,
                    initialDelayMs,
                    refreshIntervalMs,
                    TimeUnit.MILLISECONDS
            );
        } else {
            logger.info("Already active, no-op");
        }
    }

----- 上面的updateAction.doUpdate()

            public void doUpdate() {
                DynamicServerListLoadBalancer.this.updateListOfServers();
            }
  • 这不就又回来了么,restOfInit方法中我们就有一个步骤是获取所有的Eureka实例表updateListOfServers()

(8.5)updateListOfServers();

    @VisibleForTesting
    public void updateListOfServers() {
        List<T> servers = new ArrayList<T>();
        if (serverListImpl != null) {
        	// 拉取服务对应实例列表
            servers = serverListImpl.getUpdatedListOfServers();
            LOGGER.debug("List of Servers for {} obtained from Discovery client: {}",
                    getIdentifier(), servers);

            if (filter != null) {
                servers = filter.getFilteredListOfServers(servers);
                LOGGER.debug("Filtered List of Servers for {} obtained from Discovery client: {}",
                        getIdentifier(), servers);
            }
        }
        // 更新服务列表
        updateAllServerList(servers);
    }
  • 该方法的作用就是更新服务列表

(8.5.1)servers = serverListImpl.getUpdatedListOfServers();

	// DiscoveryEnabledNIWSServerList类中的方法
    @Override
    public List<DiscoveryEnabledServer> getUpdatedListOfServers(){
        return obtainServersViaDiscovery();
    }

----
    private List<DiscoveryEnabledServer> obtainServersViaDiscovery() {
        List<DiscoveryEnabledServer> serverList = new ArrayList<DiscoveryEnabledServer>();

        if (eurekaClientProvider == null || eurekaClientProvider.get() == null) {
            logger.warn("EurekaClient has not been initialized yet, returning an empty list");
            return new ArrayList<DiscoveryEnabledServer>();
        }
		// EurekaClient客户端(因为这里我们是从EurekaClient中拿服务注册列表的)
        EurekaClient eurekaClient = eurekaClientProvider.get();
        if (vipAddresses!=null){
            for (String vipAddress : vipAddresses.split(",")) {
                // if targetRegion is null, it will be interpreted as the same region of client
                // 从eurekaClient里面获取本地实例的缓存
                List<InstanceInfo> listOfInstanceInfo = eurekaClient.getInstancesByVipAddress(vipAddress, isSecure, targetRegion);
                for (InstanceInfo ii : listOfInstanceInfo) {
                    if (ii.getStatus().equals(InstanceStatus.UP)) {

                        if(shouldUseOverridePort){
                            if(logger.isDebugEnabled()){
                                logger.debug("Overriding port on client name: " + clientName + " to " + overridePort);
                            }

                            // copy is necessary since the InstanceInfo builder just uses the original reference,
                            // and we don't want to corrupt the global eureka copy of the object which may be
                            // used by other clients in our system
                            InstanceInfo copy = new InstanceInfo(ii);

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

                        DiscoveryEnabledServer des = new DiscoveryEnabledServer(ii, isSecure, shouldUseIpAddr);
                        des.setZone(DiscoveryClient.getZone(ii));
                        serverList.add(des);
                    }
                }
                if (serverList.size()>0 && prioritizeVipAddressBasedServers){
                    break; // if the current vipAddress has servers, we dont use subsequent vipAddress based servers
                }
            }
        }
        return serverList;
    }

-------
    /**
     * Gets the list of instances matching the given VIP Address in the passed region.
     *
     * @param vipAddress - The VIP address to match the instances for.
     * @param secure - true if it is a secure vip address, false otherwise
     * @param region - region from which the instances are to be fetched. If <code>null</code> then local region is
     *               assumed.
     *
     * @return - The list of {@link InstanceInfo} objects matching the criteria, empty list if not instances found.
     */
    @Override
    public List<InstanceInfo> getInstancesByVipAddress(String vipAddress, boolean secure,
                                                       @Nullable String region) {
        if (vipAddress == null) {
            throw new IllegalArgumentException(
                    "Supplied VIP Address cannot be null");
        }
        Applications applications;
        if (instanceRegionChecker.isLocalRegion(region)) {
        
            applications = this.localRegionApps.get();
        } else {
            applications = remoteRegionVsApps.get(region);
            if (null == applications) {
                logger.debug("No applications are defined for region {}, so returning an empty instance list for vip "
                        + "address {}.", region, vipAddress);
                return Collections.emptyList();
            }
        }

        if (!secure) {
            return applications.getInstancesByVirtualHostName(vipAddress);
        } else {
            return applications.getInstancesBySecureVirtualHostName(vipAddress);

        }

    }
  • 上面方法的调用链就是从Eureka Client中获取服务注册列表

(8.5.2)updateAllServerList(servers);

    /**
     * Update the AllServer list in the LoadBalancer if necessary and enabled
     * 
     * @param ls
     */
    protected void updateAllServerList(List<T> ls) {
        // other threads might be doing this - in which case, we pass
        if (serverListUpdateInProgress.compareAndSet(false, true)) {
            try {
                for (T s : ls) {
                    s.setAlive(true); // set so that clients can start using these
                                      // servers right away instead
                                      // of having to wait out the ping cycle.
                }
                // 将服务列表进行保存
                setServersList(ls);
                super.forceQuickPing();
            } finally {
                serverListUpdateInProgress.set(false);
            }
        }
    }
--------
    @Override
    public void setServersList(List lsrv) {
        super.setServersList(lsrv);
        List<T> serverList = (List<T>) lsrv;
        Map<String, List<Server>> serversInZones = new HashMap<String, List<Server>>();
        for (Server server : serverList) {
            // make sure ServerStats is created to avoid creating them on hot
            // path
            getLoadBalancerStats().getSingleServerStat(server);
            String zone = server.getZone();
            if (zone != null) {
                zone = zone.toLowerCase();
                List<Server> servers = serversInZones.get(zone);
                if (servers == null) {
                    servers = new ArrayList<Server>();
                    serversInZones.put(zone, servers);
                }
                servers.add(server);
            }
        }
        setServerListForZones(serversInZones);
    }
  • 该方法的作用就是将服务实例列表设置到父亲的BaseLoadBalancer的allServerList中

(8.6)重新回到8.2

  • DynamicServerListLoadBalancer类继承了BaseLoadBalancer类,调用方法initWithConfig
    void initWithConfig(IClientConfig clientConfig, IRule rule, IPing ping) {
        this.config = clientConfig;
        String clientName = clientConfig.getClientName();
        this.name = clientName;
        int pingIntervalTime = Integer.parseInt(""
                + clientConfig.getProperty(
                        CommonClientConfigKey.NFLoadBalancerPingInterval,
                        Integer.parseInt("30")));
        int maxTotalPingTime = Integer.parseInt(""
                + clientConfig.getProperty(
                        CommonClientConfigKey.NFLoadBalancerMaxTotalPingTime,
                        Integer.parseInt("2")));
		// 设置Server的定时Ping任务
        setPingInterval(pingIntervalTime);
        setMaxTotalPingTime(maxTotalPingTime);

        // cross associate with each other
        // i.e. Rule,Ping meet your container LB
        // LB, these are your Ping and Rule guys ...
        setRule(rule);
        setPing(ping);
        setLoadBalancerStats(new LoadBalancerStats(clientName));
        rule.setLoadBalancer(this);
        if (ping instanceof AbstractLoadBalancerPing) {
            ((AbstractLoadBalancerPing) ping).setLoadBalancer(this);
        }
        logger.info("Client: {} instantiated a LoadBalancer: {}", name, this);
        boolean enablePrimeConnections = clientConfig.get(
                CommonClientConfigKey.EnablePrimeConnections, DefaultClientConfigImpl.DEFAULT_ENABLE_PRIME_CONNECTIONS);

        if (enablePrimeConnections) {
            this.setEnablePrimingConnections(true);
            PrimeConnections primeConnections = new PrimeConnections(
                    this.getName(), clientConfig);
            this.setPrimeConnections(primeConnections);
        }
        init();

    }

setPingInterval(pingIntervalTime); 这个方法的作用就是设置Server的定时Ping任务,IPing功能就是会定时刷新Ribbon的服务列表,将不可用的服务剔除,以致于负载均衡的时候不会让其一直访问错误的服务。

Feign源码解析

在这里插入图片描述

注意

  • Feign利用了动态代理来实现,实际上底层还是Ribbon来实现的
  • Ribbon是利用拦截器来实现的
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值