第二章 SpringCloud Ribbon的源码解读
在SpringCloud中Ribbon的作用就是根据负载均衡策略从服务列表中选取合适的服务进行调用。
Ribbon调用服务的基本流程
上面这一张图从RestTemplate开始看起
1.当RestTemplate发起对远程服务的调用的时候,会调用InterceptingClieantRequest的executeInternal()方法。
2在InterceptingClieantRequest中会调用拦截器LoadBalanceInterceptor的intercept()拦截方法。
3.在LoadBalanceInterceptor的intercept()拦截方法中会调用LoadBalanceClient的实现了类RibbonLoadBalanceClient的execute方法。
4.RibbonLoadBalanceClient作为Ribbon的核心方法,在这个方法中会获取到ILoadBalancer的实例,通过ILoadBalancer实例获取到所有的服务列表和可用的服务列表,以及通过ILoadBalancer实例调用IRule的实例获取到符合策略的服务进行调用。
几个重要对象的介绍
1.RestTemplate
RestTemplate对象是对外发起请求的起点,封装请求参数发起请求,并解析返回结果,使返回结果可以直接拿来用。RestTemplate还会存储LoadBalanceInterceptor,在RestTemplate对外发起请求的时候会被LoadBalanceInterceptor拦截住,交由LoadBalanceInterceptor来处理
2.LoadBalanceInterceptor
LoadBalanceInterceptor会拦截RestTemplate对外发起的请求,但是LoadBalanceInterceptor没有做多余的处理,就是通过逻辑url获取到要调用的服务名,然后将服务名传给LoadBalanceClient,调用LoadBalanceClient来处理请求。
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {
private LoadBalancerClient loadBalancer;
private LoadBalancerRequestFactory requestFactory;
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
this.loadBalancer = loadBalancer;
this.requestFactory = requestFactory;
}
public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
// for backwards compatibility
this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
}
@Override
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body,
final ClientHttpRequestExecution execution) throws IOException {
final URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
}
}
3.LoadBalanceClient
Ribbon负载均衡的客户端,可以把它看成一个控制器,因为在它这里根据根据服务名称选取ILoadBalacer,其中ILoadBalancer是根据负载均衡策略选出服务的关键;LoadBalanceClient还会通过RibbonStatsRecorder记录服务调用的情况。例如调用的服务耗时情况,服务的并发数,这些都是在负载策略选取服务的时候被用到的参考数据。
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, LoadBalancerRequest<T> request, Object hint) throws IOException {
// 1. 获取ILoadBalancer
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
// 2. 通过ILoadBalancer选择一个Server
Server server = getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
RibbonServer ribbonServer = new RibbonServer(serviceId, server, isSecure(server,
serviceId), serverIntrospector(serviceId).getMetadata(server));
// 3. 对Server发起请求
return execute(serviceId, ribbonServer, request);
}
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");
}
首先要得到一个ILoadBalancer,用它去得到一个Server(该server就是服务实例的封装体,也是实现负载均衡的地方)。具体的代码如下:
protected Server getServer(ILoadBalancer loadBalancer) {
return loadBalancer == null ? null : loadBalancer.chooseServer("default");
}
查看负载均衡的方法可以看大其源码如下:
public Server chooseServer(Object key) {
if (this.counter == null) {
this.counter = this.createCounter();
}
this.counter.increment();
if (this.rule == null) {
return null;
} else {
try {
return this.rule.choose(key);
} catch (Exception var3) {
logger.warn("LoadBalancer [{}]: Error choosing server for key {}", new Object[]{this.name, key, var3});
return null;
}
}
}
4.ILoadBalacer
ILoadBalacer可以从EurekaClient获取相应的服务列表,并且还会让IPing对象启动定时任务,获取可用服务列表。ILoadBalacer还会还会调用IRule来选出服务进行调用
public interface ILoadBalancer {
public void addServers(List<Server> newServers);
public Server chooseServer(Object key);
public void markServerDown(Server server);
public List<Server> getReachableServers();
public List<Server> getAllServers();
}
5.IRule
IRule具体的负载均衡策略,根据具体的实现选出服务来进行调用。
public interface IRule{
public Server choose(Object key);
public void setLoadBalancer(ILoadBalancer lb);
public ILoadBalancer getLoadBalancer();
}
实现类有:
-
BestAviableRule
跳过熔断的Server,在剩下的Server中选择并发请求最低的Server - ClientConfigEnabledRoundRobinRule、RoundRobinRule
轮询 -
RandomRule
随机选择 -
RetryRule
可重试的策略,可以对其他策略进行重试,默认轮询重试 -
WeightedResponseTimeRule
根据响应时间加权,响应时间越短权重越大 -
AvailabilityFilteringRule
剔除因为连续链接、读失败或链接超过最大限制导致熔断的Server,在剩下读Server中进行轮询。
在启动Ribbon服务的时候,会加载LoadBalancerAutoConfiguration这个配置类,在这个配置类里面会维护被@LoadBalance修饰的RestTemplate对象,并对RestTemplate对象设置拦截器:
@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 loadBalancedRestTemplateInitializer(
final List<RestTemplateCustomizer> customizers) {
return new SmartInitializingSingleton() {
@Override
public void afterSingletonsInstantiated() {
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 new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate 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 RetryTemplate retryTemplate() {
RetryTemplate template = new RetryTemplate();
template.setThrowLastExceptionOnExhausted(true);
return template;
}
@Bean
@ConditionalOnMissingBean
public LoadBalancedRetryPolicyFactory loadBalancedRetryPolicyFactory() {
return new LoadBalancedRetryPolicyFactory.NeverRetryFactory();
}
@Bean
@ConditionalOnMissingBean
public LoadBalancedBackOffPolicyFactory loadBalancedBackOffPolicyFactory() {
return new LoadBalancedBackOffPolicyFactory.NoBackOffPolicyFactory();
}
@Bean
@ConditionalOnMissingBean
public LoadBalancedRetryListenerFactory loadBalancedRetryListenerFactory() {
return new LoadBalancedRetryListenerFactory.DefaultRetryListenerFactory();
}
}
@Configuration
@ConditionalOnClass(RetryTemplate.class)
public static class RetryInterceptorAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public RetryLoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties,
LoadBalancedRetryPolicyFactory lbRetryPolicyFactory,
LoadBalancerRequestFactory requestFactory,
LoadBalancedBackOffPolicyFactory backOffPolicyFactory,
LoadBalancedRetryListenerFactory retryListenerFactory) {
return new RetryLoadBalancerInterceptor(loadBalancerClient, properties,
lbRetryPolicyFactory, requestFactory, backOffPolicyFactory, retryListenerFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
return new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate restTemplate) {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
};
}
}
}
在该自动化配置类中,主要做了下面三件事:
创建了一个LoadBalancerInterceptor的Bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。
创建了一个RestTemplateCustomizer的Bean,用于给RestTemplate增加LoadBalancerInterceptor拦截器。
维护了一个被@LoadBalanced注解修饰的RestTemplate对象列表,并在这里进行初始化,通过调用RestTemplateCustomizer的实例来给需要客户端负载均衡的RestTemplate增加LoadBalancerInterceptor拦截器。
这里有一篇写的非常好的Ribbon的原理:
推荐阅读
-
jQuery选择器源码解读(四):tokenize方法的Expr.preFilter
-
SpringCloud+Eureka+Feign+Ribbon的简化搭建流程,加入熔断,网关和Redis缓存[2]
-
jQuery选择器源码解读(五):tokenize的解析过程
-
Asp.Net Core Authorize你不知道的那些事(源码解读)
-
hadoop2.6源码解读之DFSClient方法调用的RPC流程讲解
-
SpringMVC处理器适配器--源码解读前置知识HandlerMethodArgumentResolver与HandlerMethodReturnV接口的使用
-
源码解读 Golang 的 sync.Map 实现原理
-
Vue源码解读之Component组件注册的实现
-
11.Spark Streaming源码解读之Driver中的ReceiverTracker架构设计以及具体实现彻底研究
-
11.Spark Streaming源码解读之Driver中的ReceiverTracker架构设计以及具体实现彻底研究