前提

在阅读这篇博客之前,希望你对SpringCloud套件熟悉和理解,更希望关注下微服务开发平台

概述

在使用springcloud ribbon客户端负载均衡的时候,可以给RestTemplate bean 加一个@LoadBalanced注解,就能让这个RestTemplate在请求时拥有客户端负载均衡的能力,先前有细嚼过但是没有做过笔记,刚好处理此类问题记录下

@LoadBalanced

/**
* 注释将RestTemplate bean标记为配置为使用LoadBalancerClient。
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}

通过源码可以发现这是一个LoadBalanced标记注解并且标记了@Qualifier(基于Spring Boot的自动配置机制),我们可以溯源到LoadBalancerAutoConfiguration

LoadBalancerAutoConfiguration

/**
* 功能区的自动配置(客户端负载平衡)
*/
@Configuration
@ConditionalOnClass(RestTemplate.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration { @LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList(); //这里持有@LoadBalanced标记的RestTemplate实例 @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) {
//为restTemplate添加定制
customizer.customize(restTemplate);
}
}
});
} // ... /**
* 以下针对classpath存在RetryTemplate.class的情况配置,先忽略
*/
@Configuration
@ConditionalOnClass(RetryTemplate.class)
public static class RetryAutoConfiguration { @Bean
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryFactory() {
return new LoadBalancedRetryFactory() {
};
}
} // ...
}

@LoadBalanced@Autowried结合使用,意思就是这里注入的RestTempate Bean是所有加有@LoadBalanced注解标记的(持有@LoadBalanced标记的RestTemplate实例)

这段自动装配的代码的含义不难理解,就是利用了RestTempllate的拦截器,使用RestTemplateCustomizer对所有标注了@LoadBalanced的RestTemplate Bean添加了一个LoadBalancerInterceptor拦截器,而这个拦截器的作用就是对请求的URI进行转换获取到具体应该请求哪个服务实例ServiceInstance。

关键问下自己:为什么?

  • RestTemplate实例是怎么被收集的?
  • 怎样通过负载均衡规则获取具体的具体的server?

继续扒看源码>

上面可以看出,会LoadBalancerAutoConfiguration类对我们加上@LoadBalanced注解的bean 添加loadBalancerInterceptor拦截器

LoadBalancerInterceptor

/**
* 功能区的自动配置(客户端负载平衡)。
*/
public class LoadBalancerInterceptor implements ClientHttpRequestInterceptor { private LoadBalancerClient loadBalancer; private LoadBalancerRequestFactory requestFactory; public LoadBalancerInterceptor(LoadBalancerClient loadBalancer,
LoadBalancerRequestFactory requestFactory) {
this.loadBalancer = loadBalancer;
this.requestFactory = 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));
} }

重点看intercept方法 当我们restTemplate执行请求操作时,就会被拦截器拦截进入intercept方法,而loadBalancer是LoadBalancerClient的具体实现

RibbonLoadBalancerClient

	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);
}

看到这里相信都遇到过类似的错误,恍然大悟

No instances available for  xxxxx

总结

  • 1.根据serviceId 获取对应的loadBalancer
  • 2.根据loadBalancer获取具体的server(这里根据负载均衡规则,获取到具体的服务实例)
  • 3.创建RibbonServer
  • 4.执行具体请求

这里

注意: @LoadBalanced 标记注解获取到最后通过负载均衡规则获取具体的具体的server来发起请求

案例

/**
* 服务注册中心配置
*
* @author <a href="mailto:shangzhi.ibyte@gmail.com">iByte</a>
* @since 1.0.1
*/
@Configuration
@EnableConfigurationProperties(ModuleMappingHelper.class)
public class DiscoveryConfig {
@Autowired
Environment environment; /**
* DiscoveryHeaderHelper默认bean
* @return
*/
@Bean
public DiscoveryHeaderHelper discoveryHeaderHelper() {
DiscoveryHeaderHelper discoveryHeaderHelper = new DiscoveryHeaderHelper(environment);
DiscoveryHeaderHelper.INSTANCE = discoveryHeaderHelper;
return discoveryHeaderHelper;
} /**
* resttemplate构建
*/
@Resource
private RestTemplateBuilder restTemplateBuilder; /**
* resttemplate请求bean,更改系统本身的builder
* @return
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
RestTemplate restTemplate = restTemplateBuilder.configure(new RestTemplate());
//RestTemplate interceptors 远程调用请求增加头部信息处理
restTemplate.getInterceptors().add(new RestApiHeaderInterceptor());
//RestTemplate Set the error handler 错误处理
restTemplate.setErrorHandler(new RestResponseErrorHandler());
return restTemplate;
} @Bean
public DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs() {
DiscoveryClient.DiscoveryClientOptionalArgs discoveryClientOptionalArgs = new DiscoveryClient.DiscoveryClientOptionalArgs();
discoveryClientOptionalArgs.setAdditionalFilters(Collections.singletonList(new DiscoveryHeaderClientFilter()));
discoveryClientOptionalArgs.setEventListeners(Collections.singleton(new EurekaClientEventListener()));
return discoveryClientOptionalArgs;
}
}

源码地址 > DiscoveryConfig

深入理解@LoadBalanced注解的实现原理与客户端负载均衡的更多相关文章

  1. 为何一个@LoadBalanced注解就能让RestTemplate拥有负载均衡的能力?【享学Spring Cloud】

    每篇一句 你应该思考:为什么往往完成比完美更重要? 前言 在Spring Cloud微服务应用体系中,远程调用都应负载均衡.我们在使用RestTemplate作为远程调用客户端的时候,开启负载均衡极其 ...

  2. Spring Cloud(十四):Ribbon实现客户端负载均衡及其实现原理介绍

    年后到现在一直很忙,都没什么时间记录东西了,其实之前工作中积累了很多知识点,一直都堆在备忘录里,只是因为近几个月经历了一些事情,没有太多的经历来写了,但是一些重要的东西,我还是希望能坚持记录下来.正好 ...

  3. Ribbon使用及其客户端负载均衡实现原理分析

    1.ribbon负载均衡测试 (1)consumer工程添加依赖 <dependency> <groupId>org.springframework.cloud</gro ...

  4. Nginx 负载均衡原理简介与负载均衡配置详解

    Nginx负载均衡原理简介与负载均衡配置详解   by:授客  QQ:1033553122   测试环境 nginx-1.10.0 负载均衡原理 客户端向反向代理发送请求,接着反向代理根据某种负载机制 ...

  5. HA主备路由模式的原理 + HA和负载均衡的区别

       HA主备路由模式的原理 HA是High Availability缩写,即高可用性 ,可防止网络中由于单个防火墙的设备故障或网络故障导致网络中断,保证网络服务的连续性和安全强度.目前,ha功能已经 ...

  6. springcloud ribbon的 @LoadBalanced注解

    在使用springcloud ribbon客户端负载均衡的时候,可以给RestTemplate bean 加一个@LoadBalanced注解,就能让这个RestTemplate在请求时拥有客户端负载 ...

  7. Spring cloud 之Ribbon(二)负载均衡原理

    ribbon实现负载均衡的原理 我们从Ribbon实现负载均衡的代码可以看到,Ribbon是通过RestTemPlate实现客户端负载均衡的,准确的说是RestTemPlate上的@LoadBalan ...

  8. SpringCloud学习笔记(7)----Spring Cloud Netflix之负载均衡-Ribbon的深入理解

    1. 注解@LoadBalanced 作用:识别应用名称,并进行负载均衡. 2. 入口类:LoadBalancerAutoConfiguration 说明:类头上的注解可以知道Ribbon 实现的负载 ...

  9. F5负载均衡原理

    一. 负载均衡技术 负载均衡技术在现有网络结构之上提供了一种廉价.有效.透明的方法,来扩展网络设备和服务器的带宽.增加吞吐量.加强网络数据处理能力.提高网络的灵活性和可用性. 1.负载均衡发生的流程图 ...

随机推荐

  1. PHP7源码之array_unique函数分析

    以下源码基于 PHP 7.3.8 array array_unique ( array $array [, int $sort_flags = SORT_STRING ] ) (PHP 4 >= ...

  2. Circle Problem From 3Blue1Brown (分圆问题)

    Background\text{Background}Background Last night, lots of students from primary school came to our c ...

  3. opencv::Mat属性

    OpenCV支持JPG.PNG.TIFF等常见格式图像文件加载 加载图像 Mat imread( const String& filename, int flags = IMREAD_COLO ...

  4. NServiceBus+RabbitMQ开发分布式应用

    前言      NServiceBus提供了8种传输管道组件,分别是Learning.MSMQ.Azure Service Bus.Azure Service Bus (Legacy).Azure S ...

  5. python-json与字典的区别

    1.字典的类型是字典dict,是一种数据结构:json的类型是字符串str,json是一种格式: 接口测试是传参数payload时有时候是传的字符串,应该将payload的类型改为json  这点要注 ...

  6. 玩转OneNET物联网平台之MQTT服务① —— OneNetMqtt全方位调试

    授人以鱼不如授人以渔,目的不是为了教会你具体项目开发,而是学会学习的能力.希望大家分享给你周边需要的朋友或者同学,说不定大神成长之路有博哥的奠基石... QQ技术互动交流群:ESP8266&3 ...

  7. CodeForces - 1214D D. Treasure Island

    题目链接:https://vjudge.net/problem/2728294/origin 思路:可以抽象成管道,先试试能不能找到一个通道能通到终点, 如果可以则封锁这个通道,一个石头即可, 再试试 ...

  8. win32API多线程编程

    win32线程API 在Windows平台下可以通过Windows的线程库来实现多线程编程. 对于多线程程序可以使用Visual Studio调试工具进行调试,也可以使用多核芯片厂家的线程分析调试工具 ...

  9. LeetCode刷题笔记(6)按照索引计算int[] 数组中的和([Time Limit Exceeded]问题)

    Easy303 Easy633 package easy; public class e303 { private int[] sums; public e303(int[] nums) { sums ...

  10. unity单例封装

    # 1.前言本篇主要针对Unity单例模式,完成一个可以重复继承使用的抽象类,减少重复的工作与代码.同时,对存在的多种单例进行优劣分析.# 2.Unity单例问题分析## 2.1 单例原则单例要满足以 ...