网站首页 > 技术教程 正文
前言
在微服务体系中,为了保证服务高可用和高性能,服务提供者会运行多个实例并注册到注册中心中,消费者在调用提供者接口时需要选择其中具体的某个实例发起请求,此为客户端负载均衡。
springCloud基于RestTemplate抽象出负载均衡过程,消费者通过http://APP-PROVIDER/user/add这种url格式去访问提供者接口,其中 APP-PROVIDER 这个是 提供者服务的应用名(准确来说是在注册中心的serviceId,一般serviceId是应用名),那么肯定需要在发起http请求调用之前把 APP-PROVIDER 转换成 ip:port形式。 按照spring的习惯,此处应该有拦截器实现,拦截器作用是
1、根据APP-PROVIDER找到所有的运行实例
2、从诸多实例中根据一定算法挑选其中一个
3、解析成http://ip:port形式
一、@LoadBalanced
负载均衡标志注解
作用:@LoadBalanced修饰的RestTemplate实例是一个具备负载均衡能力的restTemplate
没有@LoadBalanced修饰的RestTemplate则为普通的restTemplate
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
二、ServiceInstanceChooser
serviceInstance选择器,serviceId 通常是指application name,ServiceInstance是一个实例
public interface ServiceInstanceChooser {
ServiceInstance choose(String serviceId);
}
三、LoadBalancerClient
负载均衡客户端或者负载均衡器
execute方法主要做两件事:
1、根据某种负载均衡算法通过serviceId挑选具体某个实例
2、调用LoadBalancerRequest.apply方法
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;
// 将http://APP-PROVIDER 格式 转成 http://host:port
URI reconstructURI(ServiceInstance instance, URI original);
}
四、LoadBalancerRequest
具备负载均衡功能的Request请求对象,封装好LoadBalancerRequest,再apply一下就能得到响应。apply的时机应该会在LoadBalancerClient挑选某个实例之后apply一下
public interface LoadBalancerRequest<T> {
public T apply(ServiceInstance instance) throws Exception;
}
五、LoadBalancerRequestFactory
创建LoadBalancerRequest的工厂类
public class LoadBalancerRequestFactory {
private LoadBalancerClient loadBalancer;
private List<LoadBalancerRequestTransformer> transformers;
public LoadBalancerRequest<ClientHttpResponse> createRequest(final HttpRequest request,
final byte[] body, final ClientHttpRequestExecution execution) {
//LoadBalancerRequest的apply方法实现是个lambda表达式
return instance -> {
// 利用装饰模式,将httpRequest包装成具有负载均衡能力的 LoadBalancerRequest
//然后就通过 execution.execute(serviceRequest, body)调用, 典型的拦截器执行方法
HttpRequest serviceRequest = new ServiceRequestWrapper(request, instance, loadBalancer);
if (transformers != null) {
for (LoadBalancerRequestTransformer transformer : transformers) {
serviceRequest = transformer.transformRequest(serviceRequest, instance);
}
}
return execution.execute(serviceRequest, body);
};
}
}
六、ServiceRequestWrapper
装饰模式封装普通的httpRequest,最重要的是getURI方法,可以将http://APP-PROVIDER转换成 http://ip:port
public class ServiceRequestWrapper extends HttpRequestWrapper {
private final ServiceInstance instance;
private final LoadBalancerClient loadBalancer;
public ServiceRequestWrapper(HttpRequest request, ServiceInstance instance,
LoadBalancerClient loadBalancer) {
super(request);
this.instance = instance;
this.loadBalancer = loadBalancer;
}
@Override
public URI getURI() {
URI uri = this.loadBalancer.reconstructURI(
this.instance, getRequest().getURI());
return uri;
}
}
七、LoadBalancerInterceptor
负载均衡拦截器,作用就是restTemplate发起真正的http请求之前执行负载均衡客户端的execute方法
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));
}
/**因为使用的是restTemplate,在有拦截器的场景中,参数request其实是个 InterceptingClientHttpRequest
* execution是 InterceptingRequestExecution
*/
@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);
// 每次请求都会通过LoadBalancerRequestFactory创建一个新的LoadBalancerRequest
return this.loadBalancer.execute(serviceName, requestFactory.createRequest(request, body, execution));
}
}
八、LoadBalancerAutoConfiguration
负载均衡自动配置,应用启动时会自动加载到
这个类最重要的作用就是给 利用RestTemplateCustomizer 给 restTemplate 增加一个拦截器LoadBalancerInterceptor
@Configuration
@ConditionalOnClass(RestTemplate.class) //依赖 RestTemplate
@ConditionalOnBean(LoadBalancerClient.class) //依赖具体的LoadBalancerClient实例,例如RibbonLoadBalancerClient,说明系统中一定要有一个具体的负载均衡实现才会加载此类
@EnableConfigurationProperties(LoadBalancerRetryProperties.class)
public class LoadBalancerAutoConfiguration {
//将用 @LoadBalanced修饰的restTemplate对象加载到 restTemplates
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
//SmartInitializingSingleton,在IOC所有单例被实例化后会回调,见DefaultListableBeanFactory.preInstantiateSingletons
//此处作用是等restTemplate对象和 loadBalancerInterceptor实例完之后,执行RestTemplateCustomizer.customize来给restTemplate增加拦截器
@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);
}
}
});
}
// 目前没看到具体实现,只是一个转换器
@Autowired(required = false)
private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, transformers);
}
@Configuration
@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")
static class LoadBalancerInterceptorConfig {
// 创建负载均衡拦截,看命名 ribbonInterceptor,说明springCloud还是看重 Ribbon
@Bean
public LoadBalancerInterceptor ribbonInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
// RestTemplateCustomizer用lambda表达式,
// 作用就是 将 loadBalancerInterceptor 加入到restTemplate拦截器集合。
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List<ClientHttpRequestInterceptor> list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor); //此处特别注意是把负载均衡拦截器放到最后一个,意味着最后执行负载均衡拦截器
restTemplate.setInterceptors(list);
};
}
}
/**
* 这下面是关于Retry重试方面的配置,可以放单章讲解spring-retry的模型
* retry就是指请求失败根据一定策略重新多请求几次
*/
@Configuration
@ConditionalOnClass(RetryTemplate.class)
public static class RetryAutoConfiguration {
@Bean
@ConditionalOnMissingBean
public LoadBalancedRetryFactory loadBalancedRetryFactory() {
return new LoadBalancedRetryFactory() {};
}
}
@Configuration
@ConditionalOnClass(RetryTemplate.class)
public static class 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);
};
}
}
}
九、总结
负载均衡整体过程
1、应用启动阶段
根据自动装置机制LoadBalancerAutoConfiguration,给restTemplate设置一个负载均衡拦截器LoadBalancerInterceptor
2、restTemplate请求阶段
(1)InterceptingRequestExecution会执行拦截器LoadBalancerInterceptor,LoadBalancerInterceptor执行LoadBalancerClient的execute方法
(2)LoadBalancerClient根据负载均衡算法挑选实例,并执行LoadBalancerRequest.apply
(3)LoadBalancerRequest利用装饰模式转换成ServiceRequestWrapper
(4)拦截器执行链InterceptingRequestExecution在执行的时候会调用ServiceRequestWrapper.getURI,此时会利用LoadBalancerClient 将http://APP-PROVIDER转换成http://ip:port ,再发起真正的请求。
springCloud将整个负载均衡调用过程进行抽象,利用RestTemplate (spring-web5.0里的RestTemplate实现原理 ),仅仅只是增加一个拦截器就做到了负载均衡。能够看出最重要的一个地方就是 LoadBalancerClient
十、springCloud如何整合Ribbon?
猜你喜欢
- 2024-09-25 屡败屡战RETRY第六关关卡攻略 屡屡败屡战的意思
- 2024-09-25 基于redis实现的分布式队列 基于 redis 实现分布式锁是?
- 2024-09-25 .NET 使用 mysql 时 EnableRetryOnFailure() 函数的作用及注意事项
- 2024-09-25 新连载漫画大作导视(十月篇) 十日十月漫画作家
- 2024-09-25 基于guava的重试组件Guava-Retryer
- 2024-09-25 我是怎样得到AO3内容的 我是怎样得到ao3内容的英文
- 2024-09-25 Spring Boot 中使用 spring-retry 轻松解决重试
- 2024-09-25 Java重试利器之Guava-Retryer retry java
- 2024-09-25 LabVIEW调用DLL中一、二级指针参数及打包exe运行异常的解决办法
- 2024-09-25 Harbor对接Ceph S3推镜像Retry的问题
你 发表评论:
欢迎- 05-1613步震撼淘宝大促闪光裂纹破墙立体字PS制作教程
- 05-16AI教程 | 绘制扁平的萌萌哒图标
- 05-160基础学平面设计所需了解的基础常识汇总
- 05-16自学平面设计需要多长时间?十六年职业设计总监告诉你
- 05-16平面设计都要学习哪些内容?
- 05-16李涛PS教程 高手之路PS教程 合成教程 —制作一个小星球
- 05-16Illustrator实例教程:制作炫酷的漩涡效果
- 05-16Illustrator实例教程:利用混合工具制作一朵炫酷的花
- 最近发表
- 标签列表
-
- sd分区 (65)
- raid5数据恢复 (81)
- 地址转换 (73)
- 手机存储卡根目录 (55)
- tcp端口 (74)
- project server (59)
- 双击ctrl (55)
- 鼠标 单击变双击 (67)
- debugview (59)
- 字符动画 (65)
- flushdns (57)
- ps复制快捷键 (57)
- 清除系统垃圾代码 (58)
- web服务器的架设 (67)
- 16进制转换 (69)
- xclient (55)
- ps源文件 (67)
- filezilla server (59)
- 句柄无效 (56)
- word页眉页脚设置 (59)
- ansys实例 (56)
- 6 1 3固件 (59)
- sqlserver2000挂起 (59)
- vm虚拟主机 (55)
- config (61)
本文暂时没有评论,来添加一个吧(●'◡'●)