SpringCloud的LoadBalance源码分析

欢迎查看Eetal的第二十篇博客–SpringCloud的LoadBalance源码分析

相关核心类

LoadBalanced
LoadBalancerClient
LoadBalancerAutoConfiguration
LoadBalancerInterceptor
RibbonAutoConfiguration
RibbonClassesConditions
AllNestedConditions
AbstractNestedCondition
AbstractNestedCondition.MemberMatchOutcomes
AbstractNestedCondition.MemberConditions
SpringBootCondition
ConditionOutcome

LoadBalanced注解与功能

通过在创建的RestTemplate上加入LoadBalanced注解,则该RestTemplate会自动成为负载均衡的RestTeplate,请求的url的hostname会作为服务名称去解析
用法如下

1
2
3
4
5
6
7
8
9
10
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}

public String requestTest(){
String url = "http://8781-eurekaClient/getClient2Info";
return restTemplate.getForObject(url, String.class);
}

等价于以下写法

1
2
3
4
5
6
7
8
9
10
11
12
@Autowired
RestTemplate restTemplate;

@Autowired
private LoadBalancerClient client;

@RequestMapping("/getProviderInfo")
public String getProviderInfo() {
ServiceInstance instance = client.choose("8780-eurekaClient");
String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/getInfo";
return restTemplate.getForObject(url, String.class);
}

源码分析

1.LoadBalanced注解

1
2
3
4
5
6
7
8
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {

}

这里注意,该注解被标记了@Qualifier注解,后续使用@Autowired注解注入时,如果注解的属性也被LoadBalanced注解,则只装载一样被LoadBalanced注解的符合autowired的对象
2.LoadBalnceAutoConfigure

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {

@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();

@Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

@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
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, this.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 restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}

}
......
}

@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
这个配置类依赖于加载RestTemplate类以及创建了LoadBalancerClient对象
其中会创建一个RestTemplateCustomizer提供给上方创建智能初始化的bean,该bean初始化过程会使用这个RestTemplateCustomizer,为每个loadBalanced注解的restTemplate加上一个负载均衡的拦截器
如果配置生效,根据前面对LoadBalanced注解了解,此处的集合成员restTemplates就是IOC容器中,被LoadBalanced注解了的RestTemplate
RestTemplate是我们手动的导入,接下来查找LoadBalancerClient对象,在RibbonAutoConfiguration中
拦截器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor {

private LoadBalancerClient loadBalancer;

private LoadBalancerRequestFactory 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,
this.requestFactory.createRequest(request, body, execution));
}

}

Ribbon自动配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
@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 {


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

/**
* {@link AllNestedConditions} that checks that either multiple classes are present.
*/
static class RibbonClassesConditions extends AllNestedConditions {

RibbonClassesConditions() {
super(ConfigurationPhase.PARSE_CONFIGURATION);
}

@ConditionalOnClass(IClient.class)
static class IClientPresent {

}

@ConditionalOnClass(RestTemplate.class)
static class RestTemplatePresent {

}

@ConditionalOnClass(AsyncRestTemplate.class)
static class AsyncRestTemplatePresent {

}

@ConditionalOnClass(Ribbon.class)
static class RibbonPresent {

}

}

......
}

当IOC容器没有LoadBalancerClient实例时,RibbonAutoConfiguration会自动创建一个Ribbon实现的负载均衡客户端,是其子类
这也是为什么springCloud的Eureka客户端会默认使用Ribbon作为负载均衡
因为spring-cloud-starter-netflix-eureka-client工程引入依赖了spring-cloud-starter-netflix-ribbon

@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)的意思是该要求满足该类中所有condition相关注解(程序加载了对应的四个类)
public abstract class AllNestedConditions extends AbstractNestedCondition
public abstract class AbstractNestedCondition extends SpringBootCondition implements ConfigurationCondition

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
public abstract class SpringBootCondition implements Condition {

private final Log logger = LogFactory.getLog(getClass());

@Override
public final boolean matches(ConditionContext context,
AnnotatedTypeMetadata metadata) {
//成立判断方法
String classOrMethodName = getClassOrMethodName(metadata);
try {
ConditionOutcome outcome = getMatchOutcome(context, metadata);//调用getMatchOutcome
logOutcome(classOrMethodName, outcome);
recordEvaluation(context, classOrMethodName, outcome);
return outcome.isMatch();//注意这里,outcome的match属性作为结果
}
catch (NoClassDefFoundError ex) {
throw new IllegalStateException(
"Could not evaluate condition on " + classOrMethodName + " due to "
+ ex.getMessage() + " not "
+ "found. Make sure your own configuration does not rely on "
+ "that class. This can also happen if you are "
+ "@ComponentScanning a springframework package (e.g. if you "
+ "put a @ComponentScan in the default package by mistake)",
ex);
}
catch (RuntimeException ex) {
throw new IllegalStateException(
"Error processing condition on " + getName(metadata), ex);
}
}
......
}

public abstract class AbstractNestedCondition extends SpringBootCondition
implements ConfigurationCondition {


@Override
public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata metadata) {
String className = getClass().getName();
MemberConditions memberConditions = new MemberConditions(context, className);
MemberMatchOutcomes memberOutcomes = new MemberMatchOutcomes(memberConditions);
return getFinalMatchOutcome(memberOutcomes);
}

protected static class MemberMatchOutcomes {

private final List<ConditionOutcome> all;

private final List<ConditionOutcome> matches;

private final List<ConditionOutcome> nonMatches;

public MemberMatchOutcomes(MemberConditions memberConditions) {
this.all = Collections.unmodifiableList(memberConditions.getMatchOutcomes());
List<ConditionOutcome> matches = new ArrayList<>();
List<ConditionOutcome> nonMatches = new ArrayList<>();
for (ConditionOutcome outcome : this.all) {
//是否满足加到不同属性集合
(outcome.isMatch() ? matches : nonMatches).add(outcome);
}
this.matches = Collections.unmodifiableList(matches);
this.nonMatches = Collections.unmodifiableList(nonMatches);
}
......

}
}
public abstract class AllNestedConditions extends AbstractNestedCondition {


@Override
protected ConditionOutcome getFinalMatchOutcome(MemberMatchOutcomes memberOutcomes) {
boolean match = hasSameSize(memberOutcomes.getMatches(), memberOutcomes.getAll());
//match就是判断结果
List<ConditionMessage> messages = new ArrayList<>();
messages.add(ConditionMessage.forCondition("AllNestedConditions")
.because(memberOutcomes.getMatches().size() + " matched "
+ memberOutcomes.getNonMatches().size() + " did not"));
for (ConditionOutcome outcome : memberOutcomes.getAll()) {
messages.add(outcome.getConditionMessage());
}
return new ConditionOutcome(match, ConditionMessage.of(messages));
}

private boolean hasSameSize(List<?> list1, List<?> list2) {
return list1.size() == list2.size();
}
......
}

ribbon的loadBalanceClient实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
public class RibbonLoadBalancerClient implements LoadBalancerClient {

private SpringClientFactory clientFactory;

public RibbonLoadBalancerClient(SpringClientFactory clientFactory) {
this.clientFactory = clientFactory;
}

@Override
public URI reconstructURI(ServiceInstance instance, URI original) {
Assert.notNull(instance, "instance can not be null");
String serviceId = instance.getServiceId();
RibbonLoadBalancerContext context = this.clientFactory
.getLoadBalancerContext(serviceId);

URI uri;
Server server;
if (instance instanceof RibbonServer) {
RibbonServer ribbonServer = (RibbonServer) instance;
server = ribbonServer.getServer();
uri = updateToSecureConnectionIfNeeded(original, ribbonServer);
}
else {
server = new Server(instance.getScheme(), instance.getHost(),
instance.getPort());
IClientConfig clientConfig = clientFactory.getClientConfig(serviceId);
ServerIntrospector serverIntrospector = serverIntrospector(serviceId);
uri = updateToSecureConnectionIfNeeded(original, clientConfig,
serverIntrospector, server);
}
return context.reconstructURIWithServer(server, uri);
}

@Override
public ServiceInstance choose(String serviceId) {
return choose(serviceId, null);
}

/**
* New: Select a server using a 'key'.
* @param serviceId of the service to choose an instance for
* @param hint to specify the service instance
* @return the selected {@link ServiceInstance}
*/
public ServiceInstance choose(String serviceId, Object hint) {
Server server = getServer(getLoadBalancer(serviceId), hint);
if (server == null) {
return null;
}
return new RibbonServer(serviceId, server, isSecure(server, serviceId),
serverIntrospector(serviceId).getMetadata(server));
}

@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request)
throws IOException {
return execute(serviceId, request, null);
}

/**
* New: Execute a request by selecting server using a 'key'. The hint will have to be
* the last parameter to not mess with the `execute(serviceId, ServiceInstance,
* request)` method. This somewhat breaks the fluent coding style when using a lambda
* to define the LoadBalancerRequest.
* @param <T> returned request execution result type
* @param serviceId id of the service to execute the request to
* @param request to be executed
* @param hint used to choose appropriate {@link Server} instance
* @return request execution result
* @throws IOException executing the request may result in an {@link IOException}
*/
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
throws IOException {
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
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));

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 {
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;
}
......
}

请移步

个人主页: yangyitao.top