LoadBalanced注解原理

Spring Cloud-LoadBalanced注解原理

一. 简述

Spring Cloud提供Ribbon来进行服务端负载均衡,和Eureka配合使用不需要再在服务集群架设负载均衡服务。

  1. RibbonAutoConfiguration配置生成RibbonLoadBalancerClient实例
  2. LoadBalancerInterceptorConfig配置生成LoadBalancerInterceptor实例(RibbonLoadBalancerClient实例和LoadBalancerRequestFactory实例)和RestTemplate自定义的RestTemplateCustomizer实例
  3. 通过RestTemplateCustomizer实例配置所有RestTemplate实例,即将负载均衡拦截器设置给所有RestTemplate
  4. RibbonClientConfiguration配置生成ZoneAwareLoadBalancer实例

二. 源码解析

2.1 RibbonAutoConfiguration

Ribbon基础配置类,用来生成RibbonLoadBalancerClient实例。此配置类要早于LoadBalancerAutoConfiguration前执行,因为在后者中要使用RibbonLoadBalancerClient实例。

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
119
@Configuration
// 只有在classpath下能找到这些类才会构建这个Bean
@ConditionalOnClass({IClient.class, RestTemplate.class, AsyncRestTemplate.class, Ribbon.class})
// 允许在单个类中使用多个@RibbonClient
@RibbonClients
// 控制配置类启动顺序,若有Eureka,那就在Eureka配置好后再配置它
@AutoConfigureAfter(
name = {"org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration"}
)
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
// 加载配置:ribbon.eager-load --> true的话,那么项目启动的时候就会把Client初始化好,避免第一次惩罚
@EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {
// Ribbon的配置文件
@Autowired(
required = false
)
private List<RibbonClientSpecification> configurations = new ArrayList();

@Autowired
private RibbonEagerLoadProperties ribbonEagerLoadProperties;

public RibbonAutoConfiguration() {
}

// 特征,FeaturesEndpoint这个端点('/actuator/features')会使用它
@Bean
public HasFeatures ribbonFeature() {
return HasFeatures.namedFeature("Ribbon", Ribbon.class);
}

// 它是最为重要的,是一个org.springframework.cloud.context.named.NamedContextFactory 此工厂用于创建命名的Spring容器
// 这里传入配置文件,每个不同命名空间就会创建一个新的容器(和Feign特别像)设置当前容器为父容器
@Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}

// 这个Bean是关键,若你没定义,就用系统默认提供的Client了
// RibbonLoadBalancerClient继承自LoadBalancerClient接口,是负载均衡客户端,也是负载均衡策略的调用方,内部使用和持有了SpringClientFactory
@Bean
@ConditionalOnMissingBean({LoadBalancerClient.class})
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(this.springClientFactory());
}

@Bean
@ConditionalOnClass(
name = {"org.springframework.retry.support.RetryTemplate"}
)
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryPolicyFactory(final SpringClientFactory clientFactory) {
return new RibbonLoadBalancedRetryFactory(clientFactory);
}

@Bean
@ConditionalOnMissingBean
public PropertiesFactory propertiesFactory() {
return new PropertiesFactory();
}

@Bean
@ConditionalOnProperty({"ribbon.eager-load.enabled"})
public RibbonApplicationContextInitializer ribbonApplicationContextInitializer() {
return new RibbonApplicationContextInitializer(this.springClientFactory(), this.ribbonEagerLoadProperties.getClients());
}

private static class OnRibbonRestClientCondition extends AnyNestedCondition {
public OnRibbonRestClientCondition() {
super(ConfigurationPhase.REGISTER_BEAN);
}

@ConditionalOnProperty({"ribbon.restclient.enabled"})
static class RibbonProperty {
RibbonProperty() {
}
}

/** @deprecated */
@Deprecated
@ConditionalOnProperty({"ribbon.http.client.enabled"})
static class ZuulProperty {
ZuulProperty() {
}
}
}

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({RibbonAutoConfiguration.OnRibbonRestClientCondition.class})
@interface ConditionalOnRibbonRestClient {
}

@Configuration
@ConditionalOnClass({HttpRequest.class})
@RibbonAutoConfiguration.ConditionalOnRibbonRestClient
protected static class RibbonClientHttpRequestFactoryConfiguration {
@Autowired
private SpringClientFactory springClientFactory;

protected RibbonClientHttpRequestFactoryConfiguration() {
}

@Bean
public RestTemplateCustomizer restTemplateCustomizer(final RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory) {
return (restTemplate) -> {
restTemplate.setRequestFactory(ribbonClientHttpRequestFactory);
};
}

@Bean
public RibbonClientHttpRequestFactory ribbonClientHttpRequestFactory() {
return new RibbonClientHttpRequestFactory(this.springClientFactory);
}
}
}

2.2 LoadBalancerAutoConfiguration

源码配置类,为RestTemplate配置拦截器,维护一个restTemplates数组,即所有标注@LoadBalanced的RestTemplate。通过LoadBalancerRequestTransformer

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
119
120
121
122
@Configuration
// 可见它只对RestTemplate生效
@ConditionalOnClass({RestTemplate.class})
// Spring容器内必须存在这个接口的Bean才会生效(参见:RibbonAutoConfiguration),即RibbonAutoConfiguration生成的RibbonLoadBalancerClient实例。
@ConditionalOnBean({LoadBalancerClient.class})
// retry的配置文件
@EnableConfigurationProperties({LoadBalancerRetryProperties.class})
public class LoadBalancerAutoConfiguration {
// 维护容器内所有的标注有@LoadBalanced注解的Bean,必须标注有@LoadBalanced注解
@LoadBalanced
@Autowired(
required = false
)
private List<RestTemplate> restTemplates = Collections.emptyList();

// LoadBalancerRequestTransformer接口:函数transformRequest(HttpRequest, ServiceInstance)
// Spring内部默认是没有提供任何实现类的
@Autowired(
required = false
)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();

public LoadBalancerAutoConfiguration() {
}

// 配置一个匿名的SmartInitializingSingleton,它的afterSingletonsInstantiated()方法会在所有的单例Bean初始化完成之后,再调用一个一个的处理BeanName
// 使用LoadBalancerInterceptorConfig配置好的RestTemplateCustomizer定制器,对所有的RestTemplate进行定制处理,就是将负载均衡拦截器设置给RestTemplate实例
// 函数式接口RestTemplateCustomizer下面有个lambda的实现,若调用者有需要可以书写然后扔进容器里既生效
// 若你项目中有多个RestTempalte,需要统一处理的话,写一个定制器是个不错的选择(比如统一要放置一个请求拦截器:输出日志之类的)
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(final ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {
return () -> {
restTemplateCustomizers.ifAvailable((customizers) -> {
Iterator var2 = this.restTemplates.iterator();

while(var2.hasNext()) {
RestTemplate restTemplate = (RestTemplate)var2.next();
Iterator var4 = customizers.iterator();

while(var4.hasNext()) {
RestTemplateCustomizer customizer = (RestTemplateCustomizer)var4.next();
customizer.customize(restTemplate);
}
}

});
};
}

// 这个工厂用于createRequest()生成一个LoadBalancerRequest
// 这个请求里面是包含LoadBalancerClient以及HttpRequest的
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
}

@Configuration
@ConditionalOnClass({RetryTemplate.class})
public static class RetryInterceptorAutoConfiguration {
public RetryInterceptorAutoConfiguration() {
}

@Bean
@ConditionalOnMissingBean
public RetryLoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRetryProperties properties, LoadBalancerRequestFactory requestFactory, LoadBalancedRetryFactory loadBalancedRetryFactory) {
return new RetryLoadBalancerInterceptor(loadBalancerClient, properties, requestFactory, loadBalancedRetryFactory);
}

@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(final RetryLoadBalancerInterceptor loadBalancerInterceptor) {
return (restTemplate) -> {
List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}

@Configuration
@ConditionalOnClass({RetryTemplate.class})
public static class RetryAutoConfiguration {
public RetryAutoConfiguration() {
}

@Bean
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryFactory() {
return new LoadBalancedRetryFactory() {
};
}
}

// 通过LoadBalancerInterceptor和RestTemplateCustomizer来添加拦截器
@Configuration
@ConditionalOnMissingClass({"org.springframework.retry.support.RetryTemplate"})
static class LoadBalancerInterceptorConfig {
LoadBalancerInterceptorConfig() {
}

// 这里直接使用的是requestFactory和Client构建一个拦截器对象
// LoadBalancerInterceptor实现ClientHttpRequestInterceptor接口,也是实现负载均衡的入口
@Bean
public LoadBalancerInterceptor ribbonInterceptor(LoadBalancerClient loadBalancerClient, LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}

// 向容器内放入一个RestTemplateCustomizer定制器,在RestTemplate初始化完成后,应用此定制化器在所有的实例上。
// 这个匿名实现的逻辑很简单,向所有的RestTemplate都塞入一个loadBalancerInterceptor让其具备有负载均衡的能力
// 此处有注解@ConditionalOnMissingBean。也就是说如果调用者自己定义过RestTemplateCustomizer类型的Bean,此处是不会执行的
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(final LoadBalancerInterceptor loadBalancerInterceptor) {
return (restTemplate) -> {
List<ClientHttpRequestInterceptor> list = new ArrayList(restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
}
  1. LoadBalancerAutoConfiguration要想生效类路径必须有RestTemplate,以及Spring容器内必须有LoadBalancerClient的实现Bean
  2. LoadBalancerClient的唯一实现类是:org.springframework.cloud.netflix.ribbon.RibbonLoadBalancerClient
  3. LoadBalancerInterceptor是个ClientHttpRequestInterceptor客户端请求拦截器。它的作用是在客户端发起请求之前拦截,进而实现客户端的负载均衡
  4. restTemplateCustomizer()返回的匿名定制器RestTemplateCustomizer它用来给所有的RestTemplate加上负载均衡拦截器(需要注意它的@ConditionalOnMissingBean注解~)

不难发现,负载均衡实现的核心就是一个拦截器,就是这个拦截器让一个普通的RestTemplate逆袭成为了一个具有负载均衡功能的请求器LoadBalancerInterceptor。

2.3 LoadBalancerRequestFactory

工厂类,根据LoadBalancerRequestTransformer数组批量对HttpRequest进行处理,创建LoadBalancerRequest。

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
public class LoadBalancerRequestFactory {
private LoadBalancerClient loadBalancer;
private List<LoadBalancerRequestTransformer> transformers;

public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer, List<LoadBalancerRequestTransformer> transformers) {
this.loadBalancer = loadBalancer;
this.transformers = transformers;
}

public LoadBalancerRequestFactory(LoadBalancerClient loadBalancer) {
this.loadBalancer = loadBalancer;
}

public LoadBalancerRequest<ClientHttpResponse> createRequest(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) {
// Lambda表达式,LoadBalancerRequest是一个函数式接口,T apply(ServiceInstance instance)
return (instance) -> {
HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, this.loadBalancer);
LoadBalancerRequestTransformer transformer;
//遍历并执行transformer.transformRequest
if (this.transformers != null) {
for(Iterator var6 = this.transformers.iterator(); var6.hasNext(); serviceRequest = transformer.transformRequest((HttpRequest)serviceRequest, instance)) {
transformer = (LoadBalancerRequestTransformer)var6.next();
}
}

//执行serviceRequest并返回ClientHttpResponse
return execution.execute((HttpRequest)serviceRequest, body);
};
}
}

2.4 LoadBalancerInterceptor

拦截器代码,主要逻辑在intercept函数,执行交给了LoadBalancerClient来处理,通过LoadBalancerRequestFactory来构建一个LoadBalancerRequest对象。

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 {

// 这个命名都不叫Client了,而叫loadBalancer
private LoadBalancerClient loadBalancer;
// 用于构建出一个Request
private LoadBalancerRequestFactory requestFactory;

public LoadBalancerInterceptor(LoadBalancerClient loadBalancer, LoadBalancerRequestFactory requestFactory) {
this.loadBalancer = loadBalancer;
this.requestFactory = requestFactory;
}

public LoadBalancerInterceptor(LoadBalancerClient loadBalancer) {
this(loadBalancer, new LoadBalancerRequestFactory(loadBalancer));
}

public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
URI originalUri = request.getURI();
String serviceName = originalUri.getHost();
Assert.state(serviceName != null, "Request URI does not contain a valid hostname: " + originalUri);
return (ClientHttpResponse)this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}
}

createRequest中通过ServiceRequestWrapper来执行替换URI的逻辑,ServiceRequestWrapper中将URI的获取交给了LoadBalancerClient#reconstructURI方法。

此拦截器拦截请求后把它的serviceName委托给了LoadBalancerClient去执行,根据ServiceName可能对应N多个实际的Server,因此就可以从众多的Server中运用均衡算法,挑选出一个最为合适的Server做最终的请求(它持有真正的请求执行器ClientHttpRequestExecution)。

2.5 LoadBalancerClient

请求被拦截后,最终都是委托给了LoadBalancerClient处理。

1
2
3
4
5
6
// 由使用负载平衡器选择要向其发送请求的服务器的类实现
public interface ServiceInstanceChooser {
// 从负载平衡器中为指定的服务选择Service服务实例。
// 也就是根据调用者传入的serviceId,负载均衡的选择出一个具体的实例出来
ServiceInstance choose(String serviceId);
}
1
2
3
4
5
6
7
8
9
10
// 它自己定义了三个方法
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;

// 重新构造url:把url中原来写的服务名 换掉 换成实际的
URI reconstructURI(ServiceInstance instance, URI original);
}

​ 它只有一个实现类RibbonLoadBalancerClient (ServiceInstanceChooser是有多个实现类的~)。
RibbonLoadBalancerClient。
​ 首先我们应当关注它的choose()方法:

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

@Override
public ServiceInstance choose(String serviceId) {
return choose(serviceId, null);
}
// hint:你可以理解成分组。若指定了,只会在这个偏好的分组里面去均衡选择
// 得到一个Server后,使用RibbonServer把server适配起来~~~
// 这样一个实例就选好了~~~真正请求会落在这个实例上~
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));
}

// 根据ServiceId去找到一个属于它的负载均衡器
protected ILoadBalancer getLoadBalancer(String serviceId) {
return this.clientFactory.getLoadBalancer(serviceId);
}

}

choose方法:传入serviceId,然后通过SpringClientFactory获取负载均衡器com.netflix.loadbalancer.ILoadBalancer,最终委托给它的chooseServer()方法选取到一个com.netflix.loadbalancer.Server实例,也就是说真正完成Server选取的是ILoadBalancer。ILoadBalancer以及它相关的类是一个较为庞大的体系,此处不做更多的展开。

LoadBalancerInterceptor执行的时候是直接委托执行的loadBalancer.execute()这个方法:
RibbonLoadBalancerClient:

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
// hint此处传值为null:一视同仁
// 说明:LoadBalancerRequest是通过LoadBalancerRequestFactory.createRequest(request, body, execution)创建出来的
// 它实现LoadBalancerRequest接口是用的一个匿名内部类,泛型类型是ClientHttpResponse
// 因为最终执行的显然还是执行器:ClientHttpRequestExecution.execute()
@Override
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
return execute(serviceId, request, null);
}
// public方法(非接口方法)
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint) throws IOException {
// 同上:拿到负载均衡器,然后拿到一个serverInstance实例
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
Server server = getServer(loadBalancer, hint);
if (server == null) { // 若没找到就直接抛出异常。这里使用的是IllegalStateException这个异常
throw new IllegalStateException("No instances available for " + serviceId);
}

// 把Server适配为RibbonServer isSecure:客户端是否安全
// serverIntrospector内省 参考配置文件:ServerIntrospectorProperties
RibbonServer ribbonServer = new RibbonServer(serviceId, server,
isSecure(server, serviceId), serverIntrospector(serviceId).getMetadata(server));

//调用本类的重载接口方法~~~~~
return execute(serviceId, ribbonServer, request);
}

// 接口方法:它的参数是ServiceInstance --> 已经确定了唯一的Server实例~~~
@Override
public <T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException {

// 拿到Server)(说白了,RibbonServer是execute时的唯一实现)
Server server = null;
if (serviceInstance instanceof RibbonServer) {
server = ((RibbonServer) serviceInstance).getServer();
}
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}

// 说明:执行的上下文是和serviceId绑定的
RibbonLoadBalancerContext context = this.clientFactory.getLoadBalancerContext(serviceId);
...
// 真正的向server发送请求,得到返回值
// 因为有拦截器,所以这里肯定说执行的是InterceptingRequestExecution.execute()方法
// so会调用ServiceRequestWrapper.getURI(),从而就会调用reconstructURI()方法
T returnVal = request.apply(serviceInstance);
return returnVal;
... // 异常处理
}

returnVal是一个ClientHttpResponse,最后交给handleResponse()方法来处理异常情况(若存在的话),若无异常就交给提取器提值:responseExtractor.extractData(response),这样整个请求就算全部完成了。
使用细节针对@LoadBalanced下的RestTemplate的使用,总结如下细节供以参考:

传入的String类型的url必须是绝对路径(http://…),否则抛出异常:java.lang.IllegalArgumentException: URI is not absolute

serviceId不区分大小写(http://user/...效果同http://USER/...),serviceId后不要跟port端口号

最后,需要特别指出的是:标注有@LoadBalanced的RestTemplate只能书写serviceId而不能再写IP地址/域名去发送请求了。若你的项目中两种case都有需要,请定义多个RestTemplate分别应对不同的使用场景~

三. 测试

了解了它的执行流程后,若需要本地测试(不依赖于注册中心),可以这么来做:

1
2
3
4
5
6
7
8
9
10
11
// 因为自动配置头上有@ConditionalOnMissingBean注解,所以自定义一个覆盖它的行为即可
// 此处复写它的getServer()方法,返回一个固定的(访问百度首页)即可,方便测试
@Bean
public LoadBalancerClient loadBalancerClient(SpringClientFactory factory) {
return new RibbonLoadBalancerClient(factory) {
@Override
protected Server getServer(ILoadBalancer loadBalancer, Object hint) {
return new Server("www.baidu.com", 80);
}
};
}

​ 这么一来,下面这个访问结果就是百度首页的html内容喽。

1
2
3
4
5
@Test
public void contextLoads() {
String obj = restTemplate.getForObject("http://my-serviceId", String.class);
System.out.println(obj);
}

此处my-serviceId肯定是不存在的,但得益于我上面自定义配置的LoadBalancerClient

什么,写死return一个Server实例不优雅?确实,总不能每次上线前还把这部分代码给注释掉吧,若有多个实例呢?还得自己写负载均衡算法吗?很显然Spring Cloud早早就为我们考虑到了这一点:脱离Eureka使用配置listOfServers进行客户端负载均衡调度(..listOfServers=)

对于上例我只需要在主配置文件里这么配置一下:

1
2
# ribbon.eureka.enabled=false # 若没用euraka,此配置可省略。否则不可以
my-serviceId.ribbon.listOfServers=www.baidu.com # 若有多个实例请用逗号分隔复制代码

效果完全同上。Tips:这种配置法不需要是完整的绝对路径,http://是可以省略的(new Server()方式亦可)

自己添加一个记录请求日志的拦截器可行吗?显然是可行的,示例如下:

1
2
3
4
5
6
7
8
9
10
11
12
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
RestTemplate restTemplate = new RestTemplate();
List<ClientHttpRequestInterceptor> list = new ArrayList<>();
list.add((request, body, execution) -> {
System.out.println("当前请求的URL是:" + request.getURI().toString());
return execution.execute(request, body);
});
restTemplate.setInterceptors(list);
return restTemplate;
}

这样每次客户端的请求都会打印这句话:当前请求的URI是:http://my-serviceId,一般情况(缺省情况)自定义的拦截器都会在负载均衡拦截器前面执行(因为它要执行最终的请求)。若你有必要定义多个拦截器且要控制顺序,可通过Ordered系列接口来实现~

最后的最后,一个非常非常重要的问题:

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

@Autowired + @LoadBalanced能把你配置的RestTemplate自动注入进来拿来定制呢???核心原理是什么?


参考:

🔗 为何一个@LoadBalanced注解就能让RestTemplate拥有负载均衡的能力?