看spring cloud源码分析好绕,还是坚持看完了。
先总结一下Ribbon的运行流程,可以跳过总结看下面,然后重新看总结。
- 项目启动的时候会自动的为我们加载
LoadBalancerAutoConfiguration
自动配置类,该自动配置类初始化条件是要求classpath必须要有RestTemplate
这个类,必须要有LoadBalancerClient
实现类。LoadBalancerAutoConfiguration
为我们干了二件事,第一件是创建了LoadBalancerInterceptor
拦截器bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。创建了一个
RestTemplateCustomizer
的bean,用于给RestTemplate
增加LoadBalancerInterceptor
拦截器。- 每次请求的时候都会执行
org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor
的intercept
方法,而LoadBalancerInterceptor
具有LoadBalancerClient
(客户端负载客户端)实例的一个引用,
在拦截器中通过方法获取服务名的请求url(比如http://user-service/user
),及服务名(比如user-service),然后调用负载均衡客户端的execute方法。- 执行负载客户端
RibbonLoadBalancerClient
(LoadBalancerClient的实现)的execute
方法,得到ILoadBalancer
(负载均衡器)的实现ZoneAwareLoadBalancer
,并且通过调用其chooseServer
方法获得服务列表中的一个实例,比如说user-service列表注册到eureka中一个实例。然后向其中的一个具体实例发起请求,得到结果。
源码分析
之前我们实现负载均衡是在消费端的RestTemplate
加上注解@LoadBalanced
,便可以实现负载均衡了
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
查看注解内容:
/**
* 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 {
}
这个注解给RestTemplate
做标记,标记为LoadBalancerClient
。
查看LoadBalancerClient
源码:
/**
* Represents a client side load balancer
* @author Spencer Gibb
*/
public interface LoadBalancerClient extends ServiceInstanceChooser {
/**
* 通过LoadBalancer的ServiceInstance对指定的服务执行请求操作
*/
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
/**
* 为系统构建一个合适的host:port形式的url。在分布式系统中,我们使用逻辑上的服务名称作为host来构建URI
* (替代服务实例的host:port形式)进行请求,比如说myservice/path/to/service。
*/
URI reconstructURI(ServiceInstance instance, URI original);
}
继承自接口ServiceInstanceChooser
:
/**
* Implemented by classes which use a load balancer to choose a server to
* send a request to.
*
* @author Ryan Baxter
*/
public interface ServiceInstanceChooser {
/**
* Choose a ServiceInstance from the LoadBalancer for the specified service
* @param serviceId the service id to look up the LoadBalancer
* @return a ServiceInstance that matches the serviceId
*/
ServiceInstance choose(String serviceId);
}
-
ServiceInstance choose(String serviceId):根据传入的服务名serviceId,从负载均衡器中挑选一个对应服务的实例。
-
T execute(String serviceId, LoadBalancerRequest<T> request):使用从负载均衡器中挑选出来的服务实例来执行请求内容。
-
T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request):使用从负载均衡器中挑选出来的服务实例来执行请求内容。
-
URI reconstructURI(ServiceInstance instance, URI original):为系统构建一个合适的host:port形式的url。在分布式系统中,我们使用逻辑上的服务名称作为host来构建URI(替代服务实例的host:port形式)进行请求,比如说myservice/path/to/service。
顺着LoadBalancerClient
接口的所属包org.springframework.cloud.client.loadbalancer
,我们对内容进行整理,可以得到下面的关系:
LoadBalancerAutoConfiguration
为客户端Ribbon负载均衡的自动化配置类,
@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) {
//通过调用RestTemplateCustomizer的实例来给需要的客户端负载均衡的RestTemplate增加LoadBalancerInterceptor拦截器。
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 {
//创建了一个LoadBalancerInterceptor的bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
//创建了一个RestTemplateCustomizer的bean,用于给RestTemplate增加LoadBalancerInterceptor拦截器。
return new RestTemplateCustomizer() {
@Override
public void customize(RestTemplate restTemplate) {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
}
};
}
}
@ConditionalOnClass(RestTemplate.class)
:当前项目的classpath路径下有RestTemplate这个类。
@ConditionalOnBean(LoadBalancerClient.class)
:spring容器中必须有LoadBalancerClient的实现bean
该自动化配置主要完成了三件事
- 创建了一个
LoadBalancerInterceptor
的bean,用于实现对客户端发起请求时进行拦截,以实现客户端负载均衡。 - 创建了一个
RestTemplateCustomizer
的bean,用于给RestTemplate增加LoadBalancerInterceptor
拦截器。 - 维护了一个被
@LoadBalanced
注解修饰的RestTemplate
对象列表,并在这里进行维护,通过调用RestTemplateCustomizer
的实例来给需要的客户端负载均衡的RestTemplate
增加LoadBalancerInterceptor
拦截器。
看看LoadBalancerInterceptor
拦截器是如何让一个普通的RestTemplate
变成负载均衡的:
LoadBalancerClient
是一个抽象的接口,originalUri.getHost()
获取到的是服务名,execute
函数去根据服务名来选择实例并发起实际的请求。
org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient
是实现LoadBalancerClient
接口,看其实现:
execute方法,使用从负载均衡器中挑选出来的服务实例来执行请求内容。
getServer方法:
getServer方法去调用ILoadBalancer实例的chooseServer方法
认识一下com.netflix.loadbalancer.ILoadBalancer
接口:
ILoadBalancer负载均衡器
Interface that defines the operations for a software loadbalancer. A typical
loadbalancer minimally need a set of servers to loadbalance for, a method to
mark a particular server to be out of rotation and a call that will choose a
server from the existing list of server.
定义软件负载平衡器操作的接口。 一个典型的负载均衡器最低限度地需要一组服务器来负载平衡,一种方法标记一个特定的服务器,以避免旋转和葱已有的服务列表中选择一个实例进行调用。
public interface ILoadBalancer {
//向负载均衡器中维护的实例列表增加服务实例
public void addServers(List<Server> newServers);
//从负载均衡器中挑选出一个具体的服务实例
public Server chooseServer(Object key);
//用来通知和标记负载均衡器中的某个具体实例已经停止服务,不然负载均衡器在下一次获取服务实例清单前都会认为服务实例均是正常服务的。
public void markServerDown(Server server);
/**
* @deprecated 2016-01-20 This method is deprecated in favor of the
* cleaner {@link #getReachableServers} (equivalent to availableOnly=true)
* and {@link #getAllServers} API (equivalent to availableOnly=false).
*
* Get the current list of servers.
*
* @param availableOnly if true, only live and available servers should be returned
*/
@Deprecated
public List<Server> getServerList(boolean availableOnly);
//获取当前正常服务的实例列表
public List<Server> getReachableServers();
//获取所有已知的服务实例列表,包括正常服务和停止服务实例。
public List<Server> getAllServers();
}
com.netflix.loadbalancer.Server
对象定义是一个传统的服务端节点,在该类中存储了服务节点的一些元数据信息,包括host,post以及一些部署信息等。
com.netflix.loadbalancer.ILoadBalancer
接口的一些实现,
springcloud整合Ribbon的时候选择采用的是com.netflix.loadbalancer.ZoneAwareLoadBalancer
负载均衡器。调用它的chooseServer
方法。
回到org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient
的execute
方法,
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
//从上面跟过来我们知道这边的serviceId其实就是服务名
ILoadBalancer loadBalancer = this.getLoadBalancer(serviceId);
//通过ZoneAwareLoadBalancer的chooseServer函数获取了负载均衡策略分配的服务实例对象Server之后,将其包装成RibbonServer(增加了服务名serverid,是否需要使用Https等其他信息)
Server server = this.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
的实现com.netflix.loadbalancer.ZoneAwareLoadBalancer
,将其包装成RibbonServer
,调用ZoneAwareLoadBalancer
的chooseServer
函数。
回到org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient
的execute
方法,调用LoadBalancerRequest
的apply
方法,向一个实际的具体服务实例发起请求,从而实现一开始以服务名为host的URI请求到host:port形式的实际访问地址的转换
apply
方法参数ServiceInstance
实例,ServiceInstance
类
上面说到的RibbonServer
对象就是ServiceInstance
接口的实现
我们已经可以大概理清了Spring Cloud Ribbon中实现客户端负载均衡的基本脉络,了解它是如何通过LoadBalancerInterceptor
拦截器对RestTemplate
的请求进行拦截,并利用Spring Cloud的负载均衡器LoadBalancerClient
将以逻辑服务名为host的URI转换成具体的服务实例的过程。同时通过分析LoadBalancerClient
的Ribbon实现RibbonLoadBalancerClient
,可以知道在使用Ribbon实现负载均衡器的实现,实际使用的还是Ribbon中定义的ILoadBalancer接口的实现,自动化配置会采用ZoneAwareLoadBalancer
的实例来实现客户端负载均衡。
网友评论