莫问天涯路几重,轻衫侧帽且从容。这篇文章主要讲述SpringCloud升级之路2020.0.x版-22.Spring Cloud LoadBalan相关的知识,希望能为你提供帮助。
文章图片
经过上一节的详细分析,我们知道可以通过
LoadBalancerClientFactory
知道默认配置类为 LoadBalancerClientConfiguration
. 并且获取微服务名称可以通过 environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
[
LoadBalancerClientFactory
]()public static final String NAMESPACE = "loadbalancer";
public static final String PROPERTY_NAME = NAMESPACE + ".client.name";
public LoadBalancerClientFactory() {
super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME);
}
【SpringCloud升级之路2020.0.x版-22.Spring Cloud LoadBalan】查看配置类
LoadBalancerClientConfiguration
,我们可以发现这个类主要定义两种 Bean,分别是 ReactorLoadBalancer&
lt;
ServiceInstance&
gt;
和 ServiceInstanceListSupplier
。ReactorLoadBalancer
是负载均衡器,主要提供根据服务名称获取服务实例列表并从从中选择的功能。[
ReactorLoadBalancer
]()Mono<
Response<
T>
>
choose(Request request);
在默认配置中的实现是:
[
LoadBalancerClientConfiguration
]()@Bean
@ConditionalOnMissingBean
public ReactorLoadBalancer<
ServiceInstance>
reactorServiceInstanceLoadBalancer(
Environment environment,
LoadBalancerClientFactory loadBalancerClientFactory) {
//获取微服务名称
String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);
//创建 RoundRobinLoadBalancer
//注意这里注入的是 LazyProvider,这主要因为在注册这个 Bean 的时候相关的 Bean 可能还没有被加载注册,利用 LazyProvider 而不是直接注入所需的 Bean 防止报找不到 Bean 注入的错误。
return new RoundRobinLoadBalancer(loadBalancerClientFactory.getLazyProvider(name,
ServiceInstanceListSupplier.class), name);
}
可以看出,默认配置的
ReactorLoadBalancer
实现是 RoundRobinLoadBalancer
。这个负载均衡器实现很简单,有一个原子类型的 AtomicInteger position
,从 ServiceInstanceListSupplier
中读取所有的服务实例列表,然后对于 position
原子加1,对列表大小取模,返回列表中这个位置的服务实例 ServiceInstance
。[
RoundRobinLoadBalancer
]()public Mono<
Response<
ServiceInstance>
>
choose(Request request) {
//注入的时候注入的是 Lazy Provider,这里取出真正的 Bean,也就是 ServiceInstanceListSupplier
ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider
.getIfAvailable(NoopServiceInstanceListSupplier::new);
//获取实例列表
return supplier.get(request)
.next()
//从列表中选择一个实例
.map(serviceInstances ->
processInstanceResponse(supplier, serviceInstances));
}private Response<
ServiceInstance>
processInstanceResponse(ServiceInstanceListSupplier supplier,
List<
ServiceInstance>
serviceInstances) {
Response<
ServiceInstance>
serviceInstanceResponse = getInstanceResponse(serviceInstances);
// 如果 ServiceInstanceListSupplier 也实现了 SelectedInstanceCallback,则执行下面的逻辑进行回调。SelectedInstanceCallback 就是每次负载均衡器选择实例之后进行的回调
if (supplier instanceof SelectedInstanceCallback &
&
serviceInstanceResponse.hasServer()) {
((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer());
}
return serviceInstanceResponse;
}private Response<
ServiceInstance>
getInstanceResponse(List<
ServiceInstance>
instances) {
if (instances.isEmpty()) {
return new EmptyResponse();
}
//postion 原子 +1 并取绝对值
int pos = Math.abs(this.position.incrementAndGet());
//返回对应下标的实例
ServiceInstance instance = instances.get(pos % instances.size());
return new DefaultResponse(instance);
}
ServiceInstanceListSupplier
是服务列表提供者接口:[
ServiceInstanceListSupplier
]()public interface ServiceInstanceListSupplier extends Supplier<
Flux<
List<
ServiceInstance>
>
>
{
String getServiceId();
default Flux<
List<
ServiceInstance>
>
get(Request request) {
return get();
}
static ServiceInstanceListSupplierBuilder builder() {
return new ServiceInstanceListSupplierBuilder();
}
}
spring-cloud-loadbalancer 中有很多
ServiceInstanceListSupplier
的实现,在默认配置中是通过属性配置指定实现的,这个配置项是spring.cloud.loadbalancer.configurations
。例如:[
LoadBalancerClientConfiguration
]()@Bean
@ConditionalOnBean(ReactiveDiscoveryClient.class)
@ConditionalOnMissingBean
//spring.cloud.loadbalancer.configurations 未指定或者为 default
@ConditionalOnProperty(value = "https://www.songbingjia.com/android/spring.cloud.loadbalancer.configurations", havingValue = "https://www.songbingjia.com/android/default",
matchIfMissing = true)
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
//通过 DiscoveryClient 提供实例
.withDiscoveryClient()
//开启缓存
.withCaching()
.build(context);
}@Bean
@ConditionalOnBean(ReactiveDiscoveryClient.class)
@ConditionalOnMissingBean
//如果 spring.cloud.loadbalancer.configurations 指定为 zone-preference
@ConditionalOnProperty(value = "https://www.songbingjia.com/android/spring.cloud.loadbalancer.configurations", havingValue = "https://www.songbingjia.com/android/zone-preference")
public ServiceInstanceListSupplier zonePreferenceDiscoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
//通过 DiscoveryClient 提供实例
.withDiscoveryClient()
//启用更倾向于同一个 zone 下实例的特性
.withZonePreference()
//开启缓存
.withCaching()
.build(context);
}
可以看到,可以通过
ServiceInstanceListSupplier.builder()
生成官方封装好各种特性的 ServiceInstanceListSupplier
。其实从底层实现可以看出,所有的 ServiceInstanceListSupplier
实现都是代理模式,例如对于默认配置,底层代码近似于:return//开启服务实例缓存
new CachingServiceInstanceListSupplier(
//启用通过 discoveryClient 的服务发现
new DiscoveryClientServiceInstanceListSupplier(
discoveryClient, env
)
, cacheManagerProvider.getIfAvailable()
);
除了默认配置
LoadBalancerClientConfiguration
,用户配置自定义配置则是通过 @LoadBalancerClients
和 @LoadBalancerClient
.这个原理是通过 LoadBalancerClientConfigurationRegistrar
实现的。首先,我们来看一下 LoadBalancerClientFactory
这个 NamedContextFactory
是如何创建的:[
LoadBalancerAutoConfiguration
]private final ObjectProvider<
List<
LoadBalancerClientSpecification>
>
configurations;
public LoadBalancerAutoConfiguration(ObjectProvider<
List<
LoadBalancerClientSpecification>
>
configurations) {
//注入 LoadBalancerClientSpecification List 的 provider
//在 Bean 创建的时候,进行载入,而不是注册的时候
this.configurations = configurations;
}@ConditionalOnMissingBean
@Bean
public LoadBalancerClientFactory loadBalancerClientFactory() {
//创建 LoadBalancerClientFactory
LoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory();
//读取所有的 LoadBalancerClientSpecification,设置为 LoadBalancerClientFactory 的配置
clientFactory.setConfigurations(this.configurations.getIfAvailable(Collections::emptyList));
return clientFactory;
}
那么,
LoadBalancerClientSpecification
这些 Bean 是怎么创建的呢?在 @LoadBalancerClients
和 @LoadBalancerClient
注解中,都包含 @Import(LoadBalancerClientConfigurationRegistrar.class)
。这个 @Import
加载一个 ImportBeanDefinitionRegistrar
,这里是 LoadBalancerClientConfigurationRegistrar
. ImportBeanDefinitionRegistrar
里面的方法参数包含注解元数据,以及注册 Bean 的 BeanDefinitionRegistry
。一般通过注解元数据,动态通过 BeanDefinitionRegistry
注册 Bean,在这里的实现是:[
LoadBalancerClients
]@Configuration(proxyBeanMethods = false)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Documented
@Import(LoadBalancerClientConfigurationRegistrar.class)
public @interface LoadBalancerClients {
//可以指定多个 LoadBalancerClient
LoadBalancerClient[] value() default {};
//指定所有的负载均衡配置的默认配置
Class<
?>
[] defaultConfiguration() default {};
}
[
LoadBalancerClient
]@Configuration(proxyBeanMethods = false)
@Import(LoadBalancerClientConfigurationRegistrar.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoadBalancerClient {
//name 和 value 都是微服务名称
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
//这个微服务的配置
Class<
?>
[] configuration() default {};
}
[
LoadBalancerClientConfigurationRegistrar
]@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
//获取 LoadBalancerClients 注解的元数据
Map<
String, Object>
attrs = metadata.getAnnotationAttributes(LoadBalancerClients.class.getName(), true);
if (attrs != null &
&
attrs.containsKey("value")) {
AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");
//对于 value 属性,其实就是一个 LoadBalancerClient 列表,对于每个生成一个特定微服务名字的LoadBalancerClientSpecification
for (AnnotationAttributes client : clients) {
registerClientConfiguration(registry, getClientName(client), client.get("configuration"));
}
}
//如果指定了 defaultConfiguration,则注册为 default 的配置
if (attrs != null &
&
attrs.containsKey("defaultConfiguration")) {
String name;
if (metadata.hasEnclosingClass()) {
name = "default." + metadata.getEnclosingClassName();
}
else {
name = "default." + metadata.getClassName();
}
registerClientConfiguration(registry, name, attrs.get("defaultConfiguration"));
}
//获取 LoadBalancerClient 注解的元数据
Map<
String, Object>
client = metadata.getAnnotationAttributes(LoadBalancerClient.class.getName(), true);
String name = getClientName(client);
if (name != null) {
registerClientConfiguration(registry, name, client.get("configuration"));
}
}private static void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {
//初始化 LoadBalancerClientSpecification 的 BeanDefinition,用于注册一个 LoadBalancerClientSpecification Bean
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(LoadBalancerClientSpecification.class);
//构造器参数
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
//注册 Bean
registry.registerBeanDefinition(name + ".LoadBalancerClientSpecification", builder.getBeanDefinition());
}
从代码中我们可以看出,通过使用
@LoadBalancerClients
和 @LoadBalancerClient
注解可以自动生成对应的 LoadBalancerClientSpecification
进而实现公共负载均衡配置或者特定某个微服务的负载均衡配置。文章图片
我们这一节详细分析 Spring Cloud LoadBalancer 的源代码来理解其中的原理,下一节我们将介绍在我们项目中使用 Spring Cloud LoadBalancer 要实现的功能。
文章图片
推荐阅读
- 斗鱼直播三面(说说JDK与JRE的区别是什么!)
- 本图文详细教程教你电脑插上耳机没声音怎样处理
- 本图文详细教程教你win10专业版与企业版的区别
- win10专业版密钥最新推荐
- 本图文详细教程教你电脑内存不足怎样办
- 本图文详细教程教你win10系统怎样打开管理员权限运行
- 本图文详细教程教你Win10系统怎样升级
- 本图文详细教程教你安装win7系统
- 运用win10专业版激活工具最新推荐