/**
* Annotation to mark a RestTemplate or WebClient 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 {
}
package org.springframework.cloud.netflix.ribbon;
/**
* Auto configuration for Ribbon (client side load balancing).
*
* @author Spencer Gibb
* @author Dave Syer
* @author Biju Kunjummen
*/
@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
@AutoConfigureAfter(
name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class,
AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class,
ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration {
@Autowired(required = false)
private List<RibbonClientSpecification> configurations = new ArrayList<>();
@Autowired
private RibbonEagerLoadProperties ribbonEagerLoadProperties;
@Bean
public HasFeatures ribbonFeature() {
return HasFeatures.namedFeature("Ribbon", Ribbon.class);
}
//省略其它方法
}
我们重点关注一下 LoadBalancerClient 注入:
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
/**
* A {@link Condition} that verifies that Ribbon is either not present or disabled.
*
* @author Olga Maciaszek-Sharma
* @since 2.2.1
*/
public class OnNoRibbonDefaultCondition extends AnyNestedCondition {
public OnNoRibbonDefaultCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.ribbon.enabled",
havingValue = "false")
static class RibbonNotEnabled {
}
@ConditionalOnMissingClass("org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient")
static class RibbonLoadBalancerNotPresent {
}
}
我们在使用RestTemplate类调用其他服务的时候,官方文档上都会出现以下代码:
这样如果服务端有多个服务实例提供,那么自动就有了客户端负载均衡的效果,使用RestTemplate的时候就非常的方便,在这里不得不说spring的这些大师真的很厉害。
注:本文中spring-cloud-dependencies版本为Hoxton.SR5,spring-cloud-loadbalancer 版本为2.2.3.RELEASE,spring-cloud-netflix-ribbon版本为 2.2.3.RELEASE。
LoadBalancerClient
在这里就会想,为什么加上上@LoadBalanced注解就有了这种神奇的效果。于是点看@LoadBalanced的源码,发现这个注解就是一个普通的注解而已,没有什么神奇的地方:
注意上面的注释中提到了这个注解是供
org.springframework.cloud.client.loadbalancer.LoadBalancerClient
使用的,那我们来看一眼LoadBalancerClient类,发现它是一个接口,这个接口在spring-cloud-commons的jar包中,如下:由于是个接口,说明这只是为了定义一种能力,源码上的注解说是Represents a client side load balancer,也就是提供一种客户端负载均衡的能力,这个能力还需要依赖具体的实现类去实现。
IDEA中使用快捷键
ctrl+H
发现这个类有2个实现类RibbonLoadBalancerClient 在spring-cloud-netflix-core jar里面,说明springcloud官方为了接入netflix的ribbon组件,专门写了RibbonLoadBalancerClient这个实现类。
而BlockingLoadBalancerClient 在spring-cloud-loadbalancer jar中,是spring cloud官方LoadBalancerClient 实现。
RibbonLoadBalancerClient注入
RibbonLoadBalancerClient可以找到是在RibbonAutoConfiguration类注入到spring容器中的,代码如下:
我们重点关注一下 LoadBalancerClient 注入:
BlockingLoadBalancerClient注入
BlockingLoadBalancerClient可以找到是在BlockingLoadBalancerClientAutoConfiguration类注入到spring容器中的,代码如下:
这里注意
@Conditional(OnNoRibbonDefaultCondition.class)
字面意思是Ribbon不存在时才会注入,其代码如下:到目前为止,两个LoadBalancerClient的实现类已经注入到spring容器中了,接下来我们看看哪里会使用到。
LoadBalancerAutoConfiguration
接下来我们看一看
org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration
,它位于 spring-cloud-commons jar包中,这个类就是关键所在。源码如下:我们先看一下:
这里会将容器中所有RestTemplate实例对象并执行 RestTemplateCustomizer#customize 回调方法,restTemplateCustomizers从哪里来的呢?
这里分了2种情形:容器中不存在RetryTemplate 和 存在 RetryTemplate 情况。
容器中不存在RetryTemplate 初始化如下:
可以看出,上述代码会在没有 RetryTemplate 时生成
LoadBalancerInterceptor ribbonInterceptor
,如果容器中存在RetryTemplate对象会执行以下代码:RestTemplate内部提供了拦截器这种机制,来可以对restTemplate的一些行为做干预。说明拦截器起到了客户端负载均衡的效果。
LoadBalancerInterceptor
接下来我们看看
org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor
类,如下: