Ribbon 源码解读(上)

Ribbon 源码解读


Ribbon 是spring cloud 生态中的客户端负载均衡工具,今天让我们一起走进Ribbon的源码世界。
对于ribbon我们平时的使用时这样的

  @Bean
  @LoadBalanced //添加了@LoadBalanced注解就可以使用Ribbon的负载均衡功能了
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }

看看@LoadBalanced注解内部
@LoadBalance注解跟普通的注解也没有什么两样,只是多了一个@Qualifier;那我们就继续找到该注解的配置类中去。(Spring一般在同一包下面,带有autoConfiguration后缀的是配置类)
在这里插入图片描述看下LoadBalancerAutoConfiguration内部实现

public class LoadBalancerAutoConfiguration {
	//查询容器中所有的带有@LoadBalanced注解的RestTemplate的,注入到restTemplates中
	@LoadBalanced
	@Autowired(required = false)
	private List<RestTemplate> restTemplates = Collections.emptyList();
	
	@Autowired(required = false)
	private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

	//声明一个SmartInitializingSingleton ,
	//这个bean的作用就是将我们容器中所有的restTemplateCustomizers拿出来执行customize方法,
	//也就是定制我们的restTemplate
	@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);
				}
			}
		});
	}
	//当容器中没有时,加载bean
	//@Bean注解需要的参数,参数会根据容器中bean的类型进行自动注入
	@Bean
	@ConditionalOnMissingBean
	public LoadBalancerRequestFactory loadBalancerRequestFactory(
			LoadBalancerClient loadBalancerClient) {
		return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
	}
	//负载均衡拦截器配置类
	@Configuration(proxyBeanMethods = false)
	@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
	static class LoadBalancerInterceptorConfig {

		//配置LoadBalancedInterceptor,ribbon的功能正式因为注入了这个拦截器才会实现
		@Bean
		public LoadBalancerInterceptor ribbonInterceptor(
				LoadBalancerClient loadBalancerClient,
				LoadBalancerRequestFactory requestFactory) {
			return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
		}
		//restTemplate定制化器。将拦截器设置到restTemplate中
		@Bean
		@ConditionalOnMissingBean
		public RestTemplateCustomizer restTemplateCustomizer(
				final LoadBalancerInterceptor loadBalancerInterceptor) {
			return restTemplate -> {
				List<ClientHttpRequestInterceptor> list = new ArrayList<>(
						restTemplate.getInterceptors());
				list.add(loadBalancerInterceptor);
				restTemplate.setInterceptors(list);
			};
		}
	}
}

那我们就一起来看拦截器的实现。当我们new一个LoadBalancedInteceptor时需要传入一个参数LoadBalancerInterceptor(loadBalancerClient, requestFactory);先记住这个参数,我们后面会用到。
着重看拦截器的intercept()方法

//调用loadBalancer(LoadBalancerClient)的execute方法
this.loadBalancer.execute(serviceName,
				this.requestFactory.createRequest(request, body, execution));

loadBalancedClient接口定义的方法如下

public interface LoadBalancerClient extends ServiceInstanceChooser {

	//通过serviceId 去执行任务(调用具体的提供服务的接口)
	<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
	//通过serviceId和servieInstance去执行任务,
	<T> T execute(String serviceId, ServiceInstance serviceInstance,
			LoadBalancerRequest<T> request) throws IOException;
	 //改造url方法,通过serviceName获取到真实的服务器的ip:port,然后替换之
	URI reconstructURI(ServiceInstance instance, URI original);

}

当然,LoadBalancedClient也是由Spring自动注入的,那么这个LoadBalancedClient具体实现是什么呢?是在哪里加载的呢?
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration#loadBalancerClient()方法为我们的容器注入了一个LoadBalancerClient,其具体实现为org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient#RibbonLoadBalancerClient

//层层调用上级,最终调用到这个方法内部
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
		/**根据serviceId获取到负载均衡器,一直跟踪到上层,发现这个方法是从容器中
		//取了一个ILoadBalancer的Bean,但是ILoadBalancer有很多个实现类,到底用的是哪个呢?
		*RibbonClientConfiguration这个类注入了我们ribbon客户端需要的所有配置
		*其中就包含了 ILoadBalancer=>ZoneAwareLoadBalancer
		*IPing=>DummyPing
		*IRule=>ZoneAvoidanceRule
		*所以此处我们获取到的实例是ZoneAwareLoadBalancer
		**/
		ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
		//通过负载均衡器选取一个server实例
		Server server = getServer(loadBalancer, hint);
		if (server == null) {
			throw new IllegalStateException("No instances available for " + serviceId);
		}
		//封装成RibbionServer		
		RibbonServer ribbonServer = new RibbonServer(serviceId, server,
				isSecure(server, serviceId),
				serverIntrospector(serviceId).getMetadata(server));
		return execute(serviceId, ribbonServer, request);
	}

跟踪getServer()方法

protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
		if (loadBalancer == null) {
			return null;
		}
		// Use 'default' on a null hint, or just pass it on?
		return loadBalancer.chooseServer(hint != null ? hint : "default");
	}
	//loadBalancer.chooseServer=>ZoneAwareLoadBalancer.chooseServer()
 @Override
    public Server chooseServer(Object key) {
    //对于中国地区来说,是没有zone的概念的, 所以此处getAvailableZones= 0进入super.chooseServer(key)
        if (!ENABLED.get() || getLoadBalancerStats().getAvailableZones().size() <= 1) {
            logger.debug("Zone aware logic disabled or there is only one zone");
            return super.chooseServer(key);
        }
       ...
      }
//super.chooseServer()
 public Server chooseServer(Object key) {
        if (counter == null) {
            counter = createCounter();
        }
        counter.increment();
        if (rule == null) {
            return null;
        } else {
            try {
            	//通过rule来选择具体的server实例,此时的IRule具体实现是哪个呢 ?
            	//我们在new ZoneAwareLoadBalancer()的时候注入了ZoneAvoidanceRule,并且保存到rule属性中去,所以我们这个里的这个rule是ZoneAvoidanceRule
            	//ZoneAvoidanceRule中没有choose方法,找到父类PredicateBasedRule
                return rule.choose(key);
            } catch (Exception e) {
                logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
                return null;
            }
        }
    }
//com.netflix.loadbalancer.PredicateBasedRule#choose
 public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
        /**
        *lb.getAllServers()=>BaseLoadBalancer.getAllServers()
        *发现只是返回了一个属性值,看看是在哪里赋值的,有个方法setServersList
        **/
        Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
        if (server.isPresent()) {
            return server.get();
        } else {
            return null;
        }       
    }

至此setServersList是在哪里调用的。猜测一下,所有的服务配置都是在nacos上配置的,nacos客户端会去读取所有的server信息并缓存在本地。此时用到的serverlist应该就是从nacos客户端拿的信息。那么ribbon是在什么时候去nacos客户端拿去的信息呢?我们再次回到ZoneAwareLoadBalancer的构造方法中去,在DynamicServerListLoadBalancer#DynamicServerListLoadBalancer(IClientConfig, IRule, IPing, ServerList<T>, ServerListFilter<T>, ServerListUpdater)中存在这么一行代码restOfInit(clientConfig);字面意思就是rest初始化,rest肯定会有接口访问信息,我们点进去继续跟进

void restOfInit(IClientConfig clientConfig) {
        boolean primeConnection = this.isEnablePrimingConnections();
        // turn this off to avoid duplicated asynchronous priming done in BaseLoadBalancer.setServerList()
        this.setEnablePrimingConnections(false);
        //开始一个延时任务去调用NacosServerList更新servers,该方法允许我们将一个新的实例添加到列表中
        enableAndInitLearnNewServersFeature();
		//接受延时任务的返回结果,更新serversList,也就是setServersList()
		//至此我们就拿到了从nacos client获取到的servert集合
        updateListOfServers();
        if (primeConnection && this.getPrimeConnections() != null) {
            this.getPrimeConnections()
                    .primeConnections(getReachableServers());
        }
        this.setEnablePrimingConnections(primeConnection);
        LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
    }
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值