" Spring 到底是春天的来临万物复苏,还是春转夏的干燥又炎热呢?" Spring的来临让JavaEE走向了另一个高度。便捷的开发,完美的生态。物极必反,学习Spring的成本越来越低,导致Java程序员越来越密集,越来越廉价…… 那么又有多少 Springer 耐下性子去研究Spring生态的架构和底层实现的细节呢??
版本信息如下:
Spring:5.3.23
Spring Boot:2.6.13
Spring Cloud:3.1.5
Spring Cloud LoadBalancer:3.1.5
正文:
由于原有的负载均衡组件Ribbon停止维护,而完美的Spring生态怎能允许缺少负载均衡组件呢?Spring Cloud官方自己造出了Spring Cloud LoadBalancer来代替原有的Ribbon。由于是官方自己写的组件,所以并没有像eureka、Feign那样抽出一个单独的组件包出来。放入到Spring Cloud Commons规范包中。
先会使用,再深入研究源码。由于太占用文章的排版空间,所以另起了一篇文章。对于整体流程来说不会自定义负载均衡策略也不影响,只不过是锦上添花。
Spring Cloud LoadBalancer 自定义负载均衡策略https://blog.csdn.net/qq_43799161/article/details/130125370知其然,再知其所以然,我们先需要明白一次服务远程调用的大致流程,再深入其中挖掘细节。
为了更明白LoadBalancer在其中的作用,所以我们从OpenFeign的远程调用开始分析,不懂OpenFeign的读者也没关系,当作黑盒即可。
public class FeignBlockingLoadBalancerClient implements Client {
@Override
public Response execute(Request request, Request.Options options) throws IOException {
final URI originalUri = URI.create(request.url());
// 拿到调用方的服务名。
String serviceId = originalUri.getHost();
…………
// 调用负载均衡组件,经过负载均衡策略后返回最终远程调用的服务器实体
ServiceInstance instance = loadBalancerClient.choose(serviceId, lbRequest);
…………
/**
* 解析ServiceInstance服务器实体,最终Feign发送Http请求到调用方服务器。
* */
String reconstructedUrl = loadBalancerClient.reconstructURI(instance, originalUri).toString();
Request newRequest = buildRequest(request, reconstructedUrl);
LoadBalancerProperties loadBalancerProperties = loadBalancerClientFactory.getProperties(serviceId);
return executeWithLoadBalancerLifecycleProcessing(delegate, options, newRequest, lbRequest, lbResponse,
supportedLifecycleProcessors, loadBalancerProperties.isUseRawStatusCodeInResponseData());
}
}
可以很清楚的看到,在OpenFeign中调用了负载均衡组件loadBalancerClient.choose根据负载均衡策略选取最终调用方ServiceInstance(ServiceInstance是Commons包定义的规范接口,也即是服务的实体对象),所以接下来分析loadBalancerClient.choose方法即可。
public class BlockingLoadBalancerClient implements LoadBalancerClient {
@Override
public <T> ServiceInstance choose(String serviceId, Request<T> request) {
// 拿到具体的负载均衡策略对象
ReactiveLoadBalancer<ServiceInstance> loadBalancer = loadBalancerClientFactory.getInstance(serviceId);
if (loadBalancer == null) {
return null;
}
// 发送请求到注册中心拿到注册表,然后根据负载均衡策略拿到最终的调用方。
Response<ServiceInstance> loadBalancerResponse = Mono.from(loadBalancer.choose(request)).block();
if (loadBalancerResponse == null) {
return null;
}
// 方法调用方ServiceInstance实体。
return loadBalancerResponse.getServer();
}
}
这里拿到具体的负载均衡策略对象,然后发送请求到注册中心拿到注册表,最终根据负载均衡策略得到具体的调用方。
接下来,我们分析如何拿到负载均衡对象就闭环了,所以看到loadBalancerClientFactory.getInstance方法。
public class LoadBalancerClientFactory extends NamedContextFactory<LoadBalancerClientSpecification> implements ReactiveLoadBalancer.Factory<ServiceInstance> {
@Override
public ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {
// ReactorServiceInstanceLoadBalancer这个接口是不是特别熟悉,没错就是负载均衡策略的抽象接口
return getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class);
}
public <T> T getInstance(String name, Class<T> type) {
// 拿到Spring的上下文对象
AnnotationConfigApplicationContext context = getContext(name);
try {
// 从Spring的工厂中拿到对象返回。
return context.getBean(type);
}
catch (NoSuchBeanDefinitionException e) {
// ignore
}
return null;
}
}
可以看到实现非常简单,就是从Spring工厂中拿到ReactorServiceInstanceLoadBalancer类型的对象,而ReactorServiceInstanceLoadBalancer类型就是负载均衡策略。不管是我们自定义负载均衡对象还是Spring Cloud LoadBalancer默认实现RoundRobinLoadBalancer轮训都会通过@LoadBalancerClient(就是一个@Configuration)+ @Bean 注入给Spring,而这里从Spring工厂中取出来然后回掉choose方法根据策略得到最终的调用方。这不就闭环啦~!
顺带提一下上篇文章中有提到过Spring Cloud LoadBalancer帮开发实现了RoundRobinLoadBalancer轮询和RandomLoadBalancer随机,那么顺便找一下哪里注入到Spring,不用想,开发者不需要手动注入,那肯定是Spring boot自动注入的。
总结:
先从主体入手,再慢慢深入,这对于看源码是一种很不错的学习方式~!
最后,如果本帖对您有一定的帮助,希望能点赞+关注+收藏!您的支持是给我最大的动力,后续会一直更新各种框架的使用和框架的源码解读~!