Ribbon核心源码剖析

1. Ribbon处理流程

在这里插入图片描述

2. Ribbon细节结构图(涉及到底层的一些组件/类的描述)

在这里插入图片描述
图中核心是负载均衡管理器LoadBalancer(总的协调者,相当于大脑,为了做事情,协调四肢),围 绕它周围的多有IRule、IPing等

  1. IRule:是在选择实例的时候的负载均衡策略对象
  2. IPing:是用来向服务发起心跳检测的,通过心跳检测来判断该服务是否可用
  3. ServerListFilter:根据一些规则过滤传入的服务实例列表
  4. ServerListUpdater:定义了一系列的对服务列表的更新操作

3. @LoadBalanced源码剖析

我们在RestTemplate实例上添加了一个@LoadBalanced注解,就可以实现负载均衡,我们接下来分析这个注解背后的操作(负载均衡过程)

/**
 * 将普通的RestTemplate对象使用LoadBalancerClient去配置处理
 * Annotation to mark a RestTemplate bean to be configured to use a LoadBalancerClient.
 * @author Spencer Gibb
 */
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

查看LoadBalancerClient

public interface LoadBalancerClient extends ServiceInstanceChooser {
// 根据服务执行请求内容
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws
IOException;
// 根据服务执行请求内容
<T> T execute(String serviceId, ServiceInstance serviceInstance,
LoadBalancerRequest<T> request) throws IOException;
// 拼接请求方式 传统中是ip:port 现在是服务名称:port 形式
URI reconstructURI(ServiceInstance instance, URI original);
}

到这一步,暂时看不出来什么内容,老规矩,SpringCloud充分利用了SpringBoot的自动装配特点,找spring.factories配置文件
在这里插入图片描述
然后查看RibbonAutoConfiguration

@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
//Eureka客户端配置,此处不需要关注
@AutoConfigureAfter(
		name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
//负载均衡配置,终点关注LoadBalancerAutoConfiguration
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
		AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class,
		ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration {

先终点研究LoadBalancerAutoConfiguration

@Configuration(proxyBeanMethods = false)
//只有在RestTemplate这个Class存在的时候才能装配该配置类
@ConditionalOnClass(RestTemplate.class)
//只有在LoadBalancerClient这个Bean存在的时候才能装配该配置类
@ConditionalOnBean(LoadBalancerClient.class)
//重试次数配置类
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
  1. 注入resttemplate对象到集合待用,
//声明了一个RestTemplate集合,此处会自动注入那些添加了@LoadBalanced注解的RestTemplate
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
  1. 注入resttemplate定制器
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {

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

	//向容器中注入RestTemplate定制器RestTemplateCustomizer,给RestTemplate添加一个一个拦截器LoadBalancerInterceptor
	@Bean
	@ConditionalOnMissingBean
	public RestTemplateCustomizer restTemplateCustomizer(
			final LoadBalancerInterceptor loadBalancerInterceptor) {
		return restTemplate -> {
			List<ClientHttpRequestInterceptor> list = new ArrayList<>(
					restTemplate.getInterceptors());
			//获取restTemplate之前的拦截器集合,并往里面添加一个新的拦截器LoadBalancerInterceptor
			list.add(loadBalancerInterceptor);
			restTemplate.setInterceptors(list);
		};
	}

}
  1. 使用定制器给集合中的每一个resttemplate对象添加一个拦截器
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
		final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
	return () -> restTemplateCustomizers.ifAvailable(customizers -> {
		//遍历RestTemplate,进行定制
		for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
			for (RestTemplateCustomizer customizer : customizers) {
				customizer.customize(restTemplate);
			}
		}
	});
}

到这里,我们明白,添加了注解的RestTemplate对象会被添加一个拦截器LoadBalancerInterceptor, 该拦截器就是后续拦截请求进行负载处理的。下面开始分析拦截器LoadBalancerInterceptor#intercept()方法

@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
		final ClientHttpRequestExecution execution) throws IOException {
	//获取拦截到的请求uri
	final URI originalUri = request.getURI();
	//获取uri中的服务名称
	String serviceName = originalUri.getHost();
	Assert.state(serviceName != null,
			"Request URI does not contain a valid hostname: " + originalUri);
	//private LoadBalancerClient loadBalancer 
	//下面交给LoadBalancerClient对象处理负载均衡
	return this.loadBalancer.execute(serviceName,
			this.requestFactory.createRequest(request, body, execution));
}

LoadBalancerClient对象又是在哪里注入的?回到最初的配置类RibbonAutoConfiguration,可以发现如下代码

@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
	return new RibbonLoadBalancerClient(springClientFactory());
}

在这里可以看到处理负载均衡的实现类为RibbonLoadBalancerClient,处理请求方法如下

public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
	//1. 获取一个负载均衡器,核心方法
	ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
	//2. 通过负载均衡器选择一个最终要使用的服务实例对象
	Server server = getServer(loadBalancer, hint);
	if (server == null) {
		throw new IllegalStateException("No instances available for " + serviceId);
	}
	//把server封装成RibbonServer
	RibbonServer ribbonServer = new RibbonServer(serviceId, server,
			isSecure(server, serviceId),
			serverIntrospector(serviceId).getMetadata(server));
	//3. 处理请求
	return execute(serviceId, ribbonServer, request);
}
//从容器中获取ILoadBalancer实例
protected ILoadBalancer getLoadBalancer(String serviceId) {
	return this.clientFactory.getLoadBalancer(serviceId);
}

ILoadBalancer什么时候注入?此处是通过clientFactory获取的负载均衡器,回到主配置类RibbonAutoConfiguration

@Bean
@ConditionalOnMissingBean
public SpringClientFactory springClientFactory() {
	SpringClientFactory factory = new SpringClientFactory();
	factory.setConfigurations(this.configurations);
	return factory;
}
public class SpringClientFactory extends NamedContextFactory<RibbonClientSpecification> {

	static final String NAMESPACE = "ribbon";
	
	//在构造方法中涉及到了RibbonClientConfiguration的装配
	public SpringClientFactory() {
		super(RibbonClientConfiguration.class, NAMESPACE, "ribbon.client.name");
	}

RibbonClientConfiguration中装配了大脑和肢干

@Bean
@ConditionalOnMissingBean
public IRule ribbonRule(IClientConfig config) {
	//如果配置文件中配置了负载均衡策略,以配置文件为准
	if (this.propertiesFactory.isSet(IRule.class, name)) {
		return this.propertiesFactory.get(IRule.class, config, name);
	}
	//如果没有配置,默认的负载均衡规则为ZoneAvoidanceRule
	ZoneAvoidanceRule rule = new ZoneAvoidanceRule();
	rule.initWithNiwsConfig(config);
	return rule;
}
@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);
	}
	//默认注入的负载均衡器为ZoneAwareLoadBalancer
	return new ZoneAwareLoadBalancer<>(config, rule, ping, serverList,
			serverListFilter, serverListUpdater);
}

回到上面查看通过负载均衡器选择一个最终要使用的服务实例对象方法Server server = getServer(loadBalancer, hint);

protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
	if (loadBalancer == null) {
		return null;
	}
	//hint为null
	// Use 'default' on a null hint, or just pass it on?
	return loadBalancer.chooseServer(hint != null ? hint : "default");
}

上面分析过loadBalancer默认为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);
    }
}

父类:com.netflix.loadbalancer.BaseLoadBalancer#chooseServer

public Server chooseServer(Object key) {
    if (counter == null) {
        counter = createCounter();
    }
    counter.increment();
    if (rule == null) {
        return null;
    } else {
        try {
        	//根据负载均衡策略选个一个服务实例,rule默认未ZoneAvoidanceRule,
            return rule.choose(key);
        } catch (Exception e) {
            logger.warn("LoadBalancer [{}]:  Error choosing server for key {}", name, key, e);
            return null;
        }
    }
}

上面分析过,rule默认未ZoneAvoidanceRule,它继承PredicateBasedRule类

@Override
public Server choose(Object key) {
    ILoadBalancer lb = getLoadBalancer();
    //从过滤之后的服务实例集合中根据轮询策略选择一个服务
    Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
    if (server.isPresent()) {
        return server.get();
    } else {
        return null;
    }       
}
public Optional<Server> chooseRoundRobinAfterFiltering(List<Server> servers, Object loadBalancerKey) {
    List<Server> eligible = getEligibleServers(servers, loadBalancerKey);
    if (eligible.size() == 0) {
        return Optional.absent();
    }
    //计算轮询到的实例索引值
    return Optional.of(eligible.get(incrementAndGetModulo(eligible.size())));
}
private int incrementAndGetModulo(int modulo) {
    for (;;) {
    	//获取当前服务实例索引值
        int current = nextIndex.get();
        //使用求余的方式计算下一个索引值
        int next = (current + 1) % modulo;
      	//通过cas的方式设置索引值,解决并发问题
        if (nextIndex.compareAndSet(current, next) && current < modulo)
            return current;
    }
}

获取服务实例已经分析完了,再来分析下处理请求的方法,就是下面的第三个步骤
RibbonLoadBalancerClient.execute()

public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
			throws IOException {
	//1. 获取一个负载均衡器,核心方法
	ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
	//2. 通过负载均衡器选择一个最终要使用的服务实例对象
	Server server = getServer(loadBalancer, hint);
	if (server == null) {
		throw new IllegalStateException("No instances available for " + serviceId);
	}
	//把server封装成RibbonServer
	RibbonServer ribbonServer = new RibbonServer(serviceId, server,
			isSecure(server, serviceId),
			serverIntrospector(serviceId).getMetadata(server));
	//3. 处理请求
	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);
	RibbonStatsRecorder statsRecorder = new RibbonStatsRecorder(context, server);

	try {
		//向server实例发起请求的关键步骤
		T returnVal = request.apply(serviceInstance);
		statsRecorder.recordStats(returnVal);
		return returnVal;
	}
	// catch IOException and rethrow so RestTemplate behaves correctly
	catch (IOException ex) {
		statsRecorder.recordStats(ex);
		throw ex;
	}
	catch (Exception ex) {
		statsRecorder.recordStats(ex);
		ReflectionUtils.rethrowRuntimeException(ex);
	}
	return null;
}

看一下LoadBalancerRequest是如何创建的 LoadBalancerInterceptor#intercept
在这里插入图片描述

public LoadBalancerRequest<ClientHttpResponse> createRequest(
		final HttpRequest request, final byte[] body,
		final ClientHttpRequestExecution execution) {
	return instance -> {
		HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance,
				this.loadBalancer);
		if (this.transformers != null) {
			for (LoadBalancerRequestTransformer transformer : this.transformers) {
				serviceRequest = transformer.transformRequest(serviceRequest,
						instance);
			}
		}
		return execution.execute(serviceRequest, body);
	};
}
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
    if (this.iterator.hasNext()) {
        ClientHttpRequestInterceptor nextInterceptor = (ClientHttpRequestInterceptor)this.iterator.next();
        return nextInterceptor.intercept(request, body, this);
    } else {
        HttpMethod method = request.getMethod();
        Assert.state(method != null, "No standard HTTP method");
        ClientHttpRequest delegate = InterceptingClientHttpRequest.this.requestFactory.createRequest(request.getURI(), method);
        request.getHeaders().forEach((key, value) -> {
            delegate.getHeaders().addAll(key, value);
        });
        if (body.length > 0) {
            if (delegate instanceof StreamingHttpOutputMessage) {
                StreamingHttpOutputMessage streamingOutputMessage = (StreamingHttpOutputMessage)delegate;
                streamingOutputMessage.setBody((outputStream) -> {
                    StreamUtils.copy(body, outputStream);
                });
            } else {
                StreamUtils.copy(body, delegate.getBody());
            }
        }

        return delegate.execute();
    }
}

最终调用AbstractClientHttpRequest#execute,此处,就已经到了RestTemplate底层执行的代码了,最终请求的调用还是靠的 RestTemplate

接下来,在进行负载chooseServer的时候,LoadBalancer负载均衡器中已经有了serverList,那么这个 serverList是什么时候被注入到LoadBalancer中的,它的一个机制大概是怎样的?

在这里插入图片描述
查看 RibbonClientConfiguration
在这里插入图片描述
把目光聚焦到使用这个空对象ServerList的地方

在这里插入图片描述

public ZoneAwareLoadBalancer(IClientConfig clientConfig, IRule rule,
                             IPing ping, ServerList<T> serverList, ServerListFilter<T> filter,
                             ServerListUpdater serverListUpdater) {
     //调用父类                        
    super(clientConfig, rule, ping, serverList, filter, serverListUpdater);
}
public DynamicServerListLoadBalancer(IClientConfig clientConfig, IRule rule, IPing ping,
                                         ServerList<T> serverList, ServerListFilter<T> filter,
                                         ServerListUpdater serverListUpdater) {
     super(clientConfig, rule, ping);
     this.serverListImpl = serverList;
     this.filter = filter;
     this.serverListUpdater = serverListUpdater;
     if (filter instanceof AbstractServerListFilter) {
         ((AbstractServerListFilter) filter).setLoadBalancerStats(getLoadBalancerStats());
     }
     //关键方法
     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);
    //该方法会开启一个延时定时任务,一定时间后每隔一段时间就从EurekaClient缓存中获取新的服务实例信息,然后更新到Ribbon本地缓存中
    //EurekaClient也会定时从EurekaServer更新服务信息
    enableAndInitLearnNewServersFeature();
	
	//因为上面是延时定时任务,此处立即执行一次
    updateListOfServers();
    if (primeConnection && this.getPrimeConnections() != null) {
        this.getPrimeConnections()
                .primeConnections(getReachableServers());
    }
    this.setEnablePrimingConnections(primeConnection);
    LOGGER.info("DynamicServerListLoadBalancer for client {} initialized: {}", clientConfig.getClientName(), this.toString());
}

接着分析enableAndInitLearnNewServersFeature

public void enableAndInitLearnNewServersFeature() {
    LOGGER.info("Using serverListUpdater {}", serverListUpdater.getClass().getSimpleName());
    serverListUpdater.start(updateAction);
}
protected final ServerListUpdater.UpdateAction updateAction = new ServerListUpdater.UpdateAction() {
    @Override
    public void doUpdate() {
    	//从EurekaClient缓存中获取新的服务实例信息
        updateListOfServers();
    }
};
@Override
public synchronized void start(final UpdateAction updateAction) {
    if (isActive.compareAndSet(false, true)) {
    	//定义一个线程,目的就是执行updateAction.doUpdate()方法
        final Runnable wrapperRunnable = new Runnable() {
            @Override
            public void run() {
                if (!isActive.get()) {
                    if (scheduledFuture != null) {
                        scheduledFuture.cancel(true);
                    }
                    return;
                }
                try {
                    updateAction.doUpdate();
                    lastUpdated = System.currentTimeMillis();
                } catch (Exception e) {
                    logger.warn("Failed one update cycle", e);
                }
            }
        };
		//开启一个延时定时任务,目的就是执行上面的线程任务调用updateAction.doUpdate()方法,定时更新服务信息
        scheduledFuture = getRefreshExecutor().scheduleWithFixedDelay(
                wrapperRunnable,
                initialDelayMs,
                refreshIntervalMs,
                TimeUnit.MILLISECONDS
        );
    } else {
        logger.info("Already active, no-op");
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值