系列文章:
SpringCloud 源码系列(1)— 注册中心Eureka 之 启动初始化
SpringCloud 源码系列(2)— 注册中心Eureka 之 服务注册、续约
SpringCloud 源码系列(3)— 注册中心Eureka 之 抓取注册表
SpringCloud 源码系列(4)— 注册中心Eureka 之 服务下线、故障、自我保护机制
SpringCloud 源码系列(5)— 注册中心Eureka 之 EurekaServer集群
SpringCloud 源码系列(6)— 注册中心Eureka 之 总结篇
负载均衡
RestTemplate
在研究 eureka 源码中,我们在 demo-consumer 消费者服务中定义了用 @LoadBalanced
标记的 RestTemplate
,然后使用 RestTemplate 通过服务名的形式来调用远程服务 demo-producer,然后请求会轮询到两个 demo-producer 实例上。
RestTemplate 是 Spring Resources
中一个访问第三方 RESTful API 接口的网络请求框架。RestTemplate 是用来消费 REST 服务的,所以 RestTemplate 的主要方法都与 REST 的 Http协议的一些方法紧密相连,例如 HEAD、GET、POST、PUT、DELETE 和 OPTIONS
等方法,这些方法在 RestTemplate 类对应的方法为 headForHeaders()、getForObject()、postForObject()、put() 和 delete()
等。
RestTemplate 本身是不具备负载均衡的能力的,如果 RestTemplate 未使用 @LoadBalanced
标记,就通过服务名的形式来调用,必然会报错。用 @LoadBalanced 标记后,调用 RestTemplate 的 REST 方法就会通过负载均衡的方式通过一定的策略路由到某个服务实例上,底层负责负载均衡的组件就是 Ribbon
。后面我们再来看 @LoadBalanced 是如何让 RestTemplate 具备负载均衡的能力的。
@SpringBootApplication
public class ConsumerApplication {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
}
@RestController
public class DemoController {
private final Logger logger = LoggerFactory.getLogger(getClass());
@Autowired
private RestTemplate restTemplate;
@GetMapping("/v1/id")
public ResponseEntity<String> getId() {
// 通过服务名的形式调用
ResponseEntity<String> result = restTemplate.getForEntity("http://demo-producer/v1/uuid", String.class);
String uuid = result.getBody();
logger.info("request id: {}", uuid);
return ResponseEntity.ok(uuid);
}
}
Ribbon 与负载均衡
1、负载均衡
负载均衡是指将负载分摊到多个执行单元上,负载均衡主要可以分为集中式负载均衡与进程内负载均衡:
集中式负载均衡
:指位于因特网与执行单元之间,并负责把网络请求转发到各个执行单元上,比如 Nginx、F5。集中式负载均衡也可以称为服务端负载均衡。进程内负载均衡
:是将负载均衡逻辑集成到客户端上,客户端维护了一份服务提供者的实例列表,实例列表一般会从注册中心比如 Eureka 中获取。有了实例列表,就可以通过负载均衡策略将请求分摊给多个服务提供者,从而达到负载均衡的目的。进程内负载均衡一般也称为客户端负载均衡。
Ribbon 是一个客户端负载均衡器
,可以很好地控制 HTTP 和 TCP 客户端的负载均衡行为。Ribbon 是 Netflix 公司开源的一个负载均衡组件,已经整合到 SpringCloud 生态中,它在 Spring Cloud 生态内是一个不可缺少的组件,少了它,服务便不能横向扩展。
2、Ribbon 模块
Ribbon 有很多子模块,官方文档中说明,目前 Netflix 公司主要用于生产环境的 Ribbon 子模块如下:
ribbon-loadbalancer
:可以独立使用或与其他模块一起使用的负载均衡器 API。ribbon-eureka
:Ribbon 结合 Eureka 客户端的 API,为负载均衡器提供动态服务注册列表信息。ribbon-core
:Ribbon 的核心API。
3、springcloud 与 ribbon 整合
与 eureka 整合到 springcloud 类似,springcloud 提供了对应的 spring-cloud-starter-netflix-eureka-client(server) 依赖包,ribbon 则整合到了 spring-cloud-starter-netflix-ribbon 中。一般也不需要单独引入 ribbon 的依赖包,spring-cloud-starter-netflix-eureka-client 中已经依赖了 spring-cloud-starter-netflix-ribbon。因此我们引入了 spring-cloud-starter-netflix-eureka-client 就可以使用 Ribbon 的功能了。
4、Ribbon 与 RestTemplate 整合使用
在 Spring Cloud 构建的微服务系统中,Ribbon 作为服务消费者的负载均衡器,有两种使用方式,一种是和 RestTemplate 相结合,另一种是和 Feign 相结合
。前面已经演示了带有负载均衡的 RestTemplate 的使用,下面用一张图来看看 RestTemplate 基于 Ribbon 的远程调用。
RestTemplate 负载均衡
@LoadBalanced 注解
以 RestTemplate 为切入点,来看 Ribbon 的负载均衡核心原理。那么首先就要先看看 @LoadBalanced
注解如何让 RestTemplate 具备负载均衡的能力了。
首先看 @LoadBalanced 这个注解的定义,可以得到如下信息:
- 这个注解使用
@Qualifier
标记,其它地方就可以注入 LoadBalanced 注解的 bean 对象。 - 从注释中可以了解到,@LoadBalanced 标记的 RestTemplate 或 WebClient 将使用
LoadBalancerClient
来配置 bean 对象。
/**
* Annotation to mark a RestTemplate or WebClient bean to be configured to use a LoadBalancerClient.
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
注意 @LoadBalanced 是 spring-cloud-commons
模块下 loadbalancer
包下的。
RestTemplate 负载均衡自动化配置
在 @LoadBalanced 同包下,有一个 LoadBalancerAutoConfiguration
自动化配置类,从注释也可以看出,这是客户端负载均衡 Ribbon 的自动化配置类。
从这个自动化配置类可以得到如下信息:
- 首先要有 RestTemplate 的依赖和定义了
LoadBalancerClient
对象的前提下才会触发这个自动化配置类,这也对应了前面,RestTemplate 要用 LoadBalancerClient 来配置。 - 接着可以看到这个类注入了带有
@LoadBalanced 注解的 RestTemplate 对象
,就是要对这部分对象增加负载均衡的能力。 - 从
SmartInitializingSingleton
的构造中可以看到,就是在 bean 初始化完成后,用RestTemplateCustomizer
定制化 RestTemplate。 - 再往下可以看到,RestTemplateCustomizer 其实就是向 RestTemplate 中添加了
LoadBalancerInterceptor
这个拦截器。 - 而 LoadBalancerInterceptor 的构建又需要
LoadBalancerClient 和 LoadBalancerRequestFactory
,LoadBalancerRequestFactory 则通过 LoadBalancerClient 和 LoadBalancerRequestTransformer 构造完成。
/**
* Auto-configuration for Ribbon (client-side load balancing).
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RestTemplate.class) // 有 RestTemplate 的依赖
@ConditionalOnBean(LoadBalancerClient.class) // 定义了 LoadBalancerClient 的 bean 对象
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
// 注入 @LoadBalanced 标记的 RestTemplate 对象
@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) {
// 利用 RestTemplateCustomizer 定制化 restTemplate
customizer.customize(restTemplate);
}
}
});
}
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
}
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
// 创建 LoadBalancerInterceptor 需要 LoadBalancerClient 和 LoadBalancerRequestFactory
@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());
// 向 restTemplate 添加 LoadBalancerInterceptor 拦截器
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
}
}
RestTemplate 拦截器 LoadBalancerInterceptor
LoadBalancerAutoConfiguration
自动化配置主要就是给 RestTemplate 添加了一个负载均衡拦截器 LoadBalancerInterceptor。从 setInterceptors 的参数可以看出,拦截器的类型是 ClientHttpRequestInterceptor
,如果我们想定制化 RestTemplate,就可以实现这个接口来定制化,然后还可以用 @Order
标记拦截器的先后顺序。
public void setInterceptors(List<ClientHttpRequestInterceptor> interceptors) {
if (this.interceptors != interceptors) {
this.interceptors.clear();
this.interceptors.addAll(interceptors);
// 根据 @Order 注解的顺序排序
AnnotationAwareOrderComparator.sort(this.interceptors);
}
}
interceptors 拦截器是在 RestTemplate 的父类 InterceptingHttpAccessor
中设置的, RestTemplate 的类结构如下图所示。
从 restTemplate.getForEntity("http://demo-producer/v1/uuid", String.class)
这个GET请求进去看看,是如何使用 LoadBalancerInterceptor 的。一步步进去,可以看到最终是进入到 doExecute
这个方法了。
在 doExecute 方法中,首先根据 url、method 创建一个 ClientHttpRequest
,然后利用 ClientHttpRequest 来发起请求。
protected <T> T doExecute(URI url, @Nullable HttpMethod method, @Nullable RequestCallback requestCallback,
@Nullable ResponseExtractor<T> responseExtractor) throws RestClientException {
ClientHttpResponse response = null;
try {
// 创建一个 ClientHttpRequest
ClientHttpRequest request = createRequest(url, method);
if (requestCallback != null) {
requestCallback.doWithRequest(request);
}
// 调用 ClientHttpRequest 的 execute() 方法
response = request.execute();
// 处理返回结果
handleResponse(url, method, response);
return (responseExtractor != null ? responseExtractor.extractData(response) : null);
}
catch (IOException ex) {
// ...
}
finally {
if (response != null) {
response.close();
}
}
}
protected ClientHttpRequest createRequest(URI url, HttpMethod method) throws IOException {
ClientHttpRequest request = getRequestFactory().createRequest(url, method);
initialize(request);
if (logger.isDebugEnabled()) {
logger.debug("HTTP " + method.name() + " " + url);
}
return request;
}
InterceptingHttpAccessor 中重写了父类 HttpAccessor
的 getRequestFactory
方法,父类默认的 requestFactory 是 SimpleClientHttpRequestFactory
。
重写后的 getRequestFactory 方法中,如果拦截器不为空,则基于父类默认的 SimpleClientHttpRequestFactory 和拦截器创建了 InterceptingClientHttpRequestFactory
。
public ClientHttpRequestFactory getRequestFactory() {
List<ClientHttpRequestInterceptor> interceptors = getInterceptors();
if (!CollectionUtils.isEmpty(interceptors)) {
ClientHttpRequestFactory factory = this.interceptingRequestFactory;
if (factory == null) {
// 传入 SimpleClientHttpRequestFactory 和 ClientHttpRequestInterceptor 拦截器
factory = new InterceptingClientHttpRequestFactory(super.getRequestFactory(), interceptors);
this.interceptingRequestFactory = factory;
}
return factory;
}
else {
return super.getRequestFactory();
}
}
也就是说调用了 InterceptingClientHttpRequestFactory 的 createRequest 方法来创建 ClientHttpRequest。进去可以看到,ClientHttpRequest 的实际类型就是 InterceptingClientHttpRequest
。
protected ClientHttpRequest createRequest(URI uri, HttpMethod httpMethod, ClientHttpRequestFactory requestFactory) {
return new InterceptingClientHttpRequest(requestFactory, this.interceptors, uri, httpMethod);
}
InterceptingClientHttpRequest 的类结构如下:
RestTemplate 的 doExecute 中调用 request.execute()
其实是调用了 InterceptingClientHttpRequest 父类 AbstractClientHttpRequest 中的 execute 方法。一步步进去可以发现最终其实是调用了 InterceptingClientHttpRequest
的 executeInternal
方法。
在 InterceptingClientHttpRequest 的 executeInternal 方法中,创建了 InterceptingRequestExecution
来执行请求。在 InterceptingRequestExecution 的 execute
方法中,会先遍历执行所有拦截器,然后通过 ClientHttpRequest 发起真正的 http 请求。
protected final ClientHttpResponse executeInternal(HttpHeaders headers, byte[] bufferedOutput) throws IOException {
// 创建 InterceptingRequestExecution
InterceptingRequestExecution requestExecution = new InterceptingRequestExecution();
// 请求调用
return requestExecution.execute(this, bufferedOutput);
}
private class InterceptingRequestExecution implements ClientHttpRequestExecution {
private final Iterator<ClientHttpRequestInterceptor> iterator;
public InterceptingRequestExecution() {
// 拦截器迭代器
this.iterator = interceptors.iterator();
}
@Override
public ClientHttpResponse execute(HttpRequest request, byte[] body) throws IOException {
if (this.iterator.hasNext()) {
ClientHttpRequestInterceptor nextInterceptor = this.iterator.next();
// 利用拦截器拦截处理,并传入 InterceptingRequestExecution
return nextInterceptor.intercept(request, body, this);
}
else {
// 拦截器遍历完后开始发起真正的 http 请求
HttpMethod method = request.getMethod();
ClientHttpRequest delegate = requestFactory.createRequest(request.getURI(), method);
request.getHeaders().forEach((key, value) -> delegate.getHeaders().addAll(key, value));
//...
return delegate.execute();
}
}
}
进入到 LoadBalancerInterceptor 的 intercept
拦截方法内,可以看到从请求的原始地址中获取了服务名称,然后调用了 loadBalancer 的 execute
方法,也就是 LoadBalancerClient
。
到这里,其实已经可以想象,loadBalancer.execute
这行代码就是根据服务名称去获取一个具体的实例,然后将原始地址替换为实例的IP地址。那这个 loadBalancer 又是什么呢?
public ClientHttpResponse intercept(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) throws IOException {
// 原始地址:http://demo-producer/v1/uuid
final URI originalUri = request.getURI();
// host 就是服务名:demo-producer
String serviceName = originalUri.getHost();
return this.loadBalancer.execute(serviceName, this.requestFactory.createRequest(request, body, execution));
}
负载均衡客户端 LoadBalancerClient
在配置 LoadBalancerInterceptor 时,需要两个参数,LoadBalancerClient
和 LoadBalancerRequestFactory
,LoadBalancerRequestFactory 前面已经知道是如何创建的了。LoadBalancerClient 又是在哪创建的呢?通过 IDEA 搜索,可以发现是在 spring-cloud-netflix-ribbon 模块下的 RibbonAutoConfiguration
中配置的,可以看到 LoadBalancerClient 的实际类型是 RibbonLoadBalancerClient
。
配置类的顺序是 EurekaClientAutoConfiguration
、RibbonAutoConfiguration
、LoadBalancerAutoConfiguration
,因为使 RestTemplate 具备负载均衡的能力需要 LoadBalancerInterceptor 拦截器,创建 LoadBalancerInterceptor 又需要 LoadBalancerClient,而 LoadBalancerClient 底层要根据服务名获取某个实例,肯定又需要一个实例库,比如从配置文件、注册中心获取。从这里就可以看出来,RibbonLoadBalancerClient 默认会从 Eureka 注册中心获取实例。
@Configuration
@Conditional(RibbonAutoConfiguration.RibbonClassesConditions.class)
@RibbonClients
// 后于 EurekaClientAutoConfiguration 配置
@AutoConfigureAfter(name = "org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration")
// 先于 LoadBalancerAutoConfiguration 配置
@AutoConfigureBefore({ LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class })
@EnableConfigurationProperties({ RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class })
public class RibbonAutoConfiguration {
@Autowired(required = false)
private List<RibbonClientSpecification> configurations = new ArrayList<>();
@Bean
@ConditionalOnMissingBean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
}
LoadBalancerClient 主要提供了三个接口:
public interface LoadBalancerClient extends ServiceInstanceChooser {
// 从 LoadBalancer 找一个 Server 来发送请求
<T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException;
// 从传入的 ServiceInstance 取 Server 来发送请求
<T> T execute(String serviceId, ServiceInstance serviceInstance, LoadBalancerRequest<T> request) throws IOException;
// 对原始 URI 重构
URI reconstructURI(ServiceInstance instance, URI original);
}
进入到 RibbonLoadBalancerClient 的 execute 方法中可以看到:
- 首先根据服务名获取服务对应的负载均衡器
ILoadBalancer
。 - 然后从 ILoadBalancer 中根据一定策略选出一个实例
Server
。 - 然后将 server、serviceId 等信息封装到
RibbonServer
中,也就是一个服务实例 ServiceInstance。 - 最后调用了 LoadBalancerRequest 的
apply
,并传入 ServiceInstance,将地址中的服务名替换为真实的IP地址。
public <T> T execute(String serviceId, LoadBalancerRequest<T> request) throws IOException {
return execute(serviceId, request, null);
}
public <T> T execute(String serviceId, LoadBalancerRequest<T> request, Object hint)
throws IOException {
// 根据服务名获取一个负载均衡器 ILoadBalancer
ILoadBalancer loadBalancer = getLoadBalancer(serviceId);
// 利用负载均衡器获取实例 Server
Server server = getServer(loadBalancer, hint);
if (server == null) {
throw new IllegalStateException("No instances available for " + serviceId);
}
// 封装实例信息:RibbonServer 的父类是 ServiceInstance
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);
}
try {
// 处理地址,将服务名替换为真实的IP地址
T returnVal = request.apply(serviceInstance);
return returnVal;
} catch (Exception ex) {
// ...
}
return null;
}
这个 LoadBalancerRequest 其实就是 LoadBalancerInterceptor 的 intercept 中创建的一个匿名类,在它的函数式接口内,主要是用装饰器 ServiceRequestWrapper
将 request 包了一层。
public LoadBalancerRequest<ClientHttpResponse> createRequest(final HttpRequest request, final byte[] body, final ClientHttpRequestExecution execution) {
return instance -> {
// 封装 HttpRequest,ServiceRequestWrapper 重载了 getURI 方法。
HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, this.loadBalancer);
if (this.transformers != null) {
for (LoadBalancerRequestTransformer transformer : this.transformers) {
serviceRequest = transformer.transformRequest(serviceRequest, instance);
}
}
// 继续执行拦截器
return execution.execute(serviceRequest, body);
};
}
ServiceRequestWrapper 主要就是重写了 getURI
方法,在重写的 getURI 方法内,它用 loadBalancer 对 URI 进行了重构,进去可以发现,就是将原始地址中的服务名替换为 Server 的真实IP、端口地址。
@Override
public URI getURI() {
// 重构 URI
URI uri = this.loadBalancer.reconstructURI(this.instance, getRequest().getURI());
return uri;
}
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);
}
reconstructURIWithServer
:
public URI reconstructURIWithServer(Server server, URI original) {
String host = server.getHost();
int port = server.getPort();
String scheme = server.getScheme();
if (host.equals(original.getHost())
&& port == original.getPort()
&& scheme == original.getScheme()) {
return original;
}
if (scheme == null) {
scheme = original.getScheme();
}
if (scheme == null) {
scheme = deriveSchemeAndPortFromPartialUri(original).first();
}
try {
StringBuilder sb = new StringBuilder();
sb.append(scheme).append("://");
if (!Strings.isNullOrEmpty(original.getRawUserInfo())) {
sb.append(original.getRawUserInfo()).append("@");
}
sb.append(host);
if (port >= 0) {
sb.append(":").append(port);
}
sb.append(original.getRawPath());
if (!Strings.isNullOrEmpty(original.getRawQuery())) {
sb.append("?").append(original.getRawQuery());
}
if (!Strings.isNullOrEmpty(original.getRawFragment())) {
sb.append("#").append(original.getRawFragment());
}
URI newURI = new URI(sb.toString());
return newURI;
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
}
RestTemplate 负载均衡总结
到这里,我们基本就弄清楚了一个简单的 @LoadBalanced
注解如何让 RestTemplate 具备了负载均衡的能力了,这一节来做个小结。
1、RestTemplate 如何获得负载均衡的能力
- 1)首先 RestTemplate 是 spring-web 模块下一个访问第三方 RESTful API 接口的网络请求框架
- 2)在 spring cloud 微服务架构中,用 @LoadBalanced 对 RestTemplate 做个标记,就可以使 RestTemplate 具备负载均衡的能力
- 3)使 RestTemplate 具备负载均衡的核心组件就是
LoadBalancerAutoConfiguration
配置类中向其添加的LoadBalancerInterceptor
负载均衡拦截器 - 4)RestTemplate 在发起 http 调用前,会遍历所有拦截器来对 RestTemplate 定制化,LoadBalancerInterceptor 就是在这时将URI中的服务名替换为实例的真实IP地址。定制完成后,就会发起真正的 http 请求。
- 5)LoadBalancerInterceptor 又主要是使用负载均衡客户端 LoadBalancerClient 来完成URI的重构的,LoadBalancerClient 就可以根据服务名查找一个可用的实例,然后重构URI。
2、核心组件
这里会涉及多个模块,下面是核心组件的所属模块:
spring-web
:
- RestTemplate
- InterceptingClientHttpRequest:执行拦截器,并发起最终http调用
spring-cloud-commons
:
- @LoadBalanced
- LoadBalancerAutoConfiguration
- LoadBalancerRequestFactory:创建装饰类 ServiceRequestWrapper 替换原来的 HttpRequest,重载 getURI 方法。
- LoadBalancerInterceptor:负载均衡拦截器
- LoadBalancerClient:负载均衡客户端接口
spring-cloud-netflix-ribbon
:
- RibbonLoadBalancerClient:LoadBalancerClient 的实现类,Ribbon 的负载均衡客户端
- RibbonAutoConfiguration
ribbon-loadbalancer
:
- ILoadBalancer:负载均衡器
- Server:实例
3、最后再用一张图把 RestTemplate 这块的关系捋一下