[spring-cloud: @LoadBalanced @LoadBalancerClient]-源码分析
源码
LoadBalanced
@LoadBalanced
注解用于标记 RestTemplate
、RestClient.Builder
或 WebClient.Builder
Bean,使其自动集成负载均衡客户端,实现在请求时自动通过负载均衡器选择服务实例。
// LoadBalancerRestClientBuilderBeanPostProcessor
// LoadBalancerWebClientBuilderBeanPostProcessor
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {}
LoadBalancerAutoConfiguration
@AutoConfiguration
@Conditional(BlockingRestClassesPresentCondition.class)
@ConditionalOnBean(LoadBalancerClient.class)
@EnableConfigurationProperties(LoadBalancerClientsProperties.class)
public class LoadBalancerAutoConfiguration {// 被标记为 @LoadBalanced 的 RestTemplate,会被注入到 restTemplates 变量中@LoadBalanced@Autowired(required = false)private List<RestTemplate> restTemplates = Collections.emptyList();@Autowired(required = false)private List<LoadBalancerRequestTransformer> transformers = Collections.emptyList();@Beanpublic SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(ObjectProvider<List<RestTemplateCustomizer>> restTemplateCustomizers) {return () -> restTemplateCustomizers.ifAvailable(customizers -> {for (RestTemplate restTemplate : restTemplates) {// LoadBalancerInterceptorConfig.restTemplateCustomizer: // LoadBalancerInterceptor// RetryInterceptorAutoConfiguration.restTemplateCustomizer: // RetryLoadBalancerInterceptorfor (RestTemplateCustomizer customizer : customizers) {customizer.customize(restTemplate);}}});}@Bean@ConditionalOnMissingBeanpublic LoadBalancerRequestFactory loadBalancerRequestFactory(LoadBalancerClient loadBalancerClient) {return new LoadBalancerRequestFactory(loadBalancerClient, transformers);}// DeferringLoadBalancerInterceptorConfig// LoadBalancerInterceptorConfig // RetryInterceptorAutoConfiguration
}
@AutoConfiguration
static class DeferringLoadBalancerInterceptorConfig {@Bean@ConditionalOnMissingBeanpublic DeferringLoadBalancerInterceptor deferringLoadBalancerInterceptor(ObjectProvider<BlockingLoadBalancerInterceptor> loadBalancerInterceptorObjectProvider) {return new DeferringLoadBalancerInterceptor(loadBalancerInterceptorObjectProvider);}@Bean@ConditionalOnBean(DeferringLoadBalancerInterceptor.class)@ConditionalOnMissingBeanLoadBalancerRestClientBuilderBeanPostProcessor lbRestClientPostProcessor(DeferringLoadBalancerInterceptor loadBalancerInterceptor, ApplicationContext context) {return new LoadBalancerRestClientBuilderBeanPostProcessor(loadBalancerInterceptor, context);}}
@AutoConfiguration
@Conditional(RetryMissingOrDisabledCondition.class)
static class LoadBalancerInterceptorConfig {@Beanpublic LoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient,LoadBalancerRequestFactory requestFactory) {return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);}@Bean@ConditionalOnMissingBeanpublic RestTemplateCustomizer restTemplateCustomizer(LoadBalancerInterceptor loadBalancerInterceptor) {return restTemplate -> {List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);restTemplate.setInterceptors(list);};}}private static class RetryMissingOrDisabledCondition extends AnyNestedCondition {RetryMissingOrDisabledCondition() {super(ConfigurationPhase.REGISTER_BEAN);}@ConditionalOnMissingClass("org.springframework.retry.support.RetryTemplate")static class RetryTemplateMissing {}@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", havingValue = "false")static class RetryDisabled {}}
/*** Auto configuration for retry mechanism.*/
@AutoConfiguration
@ConditionalOnClass(RetryTemplate.class)
public static class RetryAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic LoadBalancedRetryFactory loadBalancedRetryFactory() {return new LoadBalancedRetryFactory() {};}}/*** Auto configuration for retry intercepting mechanism.*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(RetryTemplate.class)
@ConditionalOnBean(ReactiveLoadBalancer.Factory.class)
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.retry.enabled", matchIfMissing = true)
public static class RetryInterceptorAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic RetryLoadBalancerInterceptor loadBalancerInterceptor(LoadBalancerClient loadBalancerClient,LoadBalancerRequestFactory requestFactory, LoadBalancedRetryFactory loadBalancedRetryFactory,ReactiveLoadBalancer.Factory<ServiceInstance> loadBalancerFactory) {return new RetryLoadBalancerInterceptor(loadBalancerClient, requestFactory, loadBalancedRetryFactory,loadBalancerFactory);}@Bean@ConditionalOnMissingBeanpublic RestTemplateCustomizer restTemplateCustomizer(RetryLoadBalancerInterceptor loadBalancerInterceptor) {return restTemplate -> {List<ClientHttpRequestInterceptor> list = new ArrayList<>(restTemplate.getInterceptors());list.add(loadBalancerInterceptor);restTemplate.setInterceptors(list);};}
}
LoadBalancerClient
@LoadBalancerClient
注解用于配置一个负载均衡客户端,通常应用于 @Configuration
类中,结合 LoadBalancerClientFactory
可获取配置的负载均衡客户端。它允许指定客户端名称、配置类等自定义负载均衡行为。
@Configuration(proxyBeanMethods = false)
@Import(LoadBalancerClientConfigurationRegistrar.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoadBalancerClient {@AliasFor("name")String value() default "";@AliasFor("value")String name() default "";/*** A custom <code>@Configuration</code> for the load balancer client. Can contain* override <code>@Bean</code> definition for the pieces that make up the client.** @see LoadBalancerClientConfiguration for the defaults* @return configuration classes for the load balancer client.*/Class<?>[] configuration() default {};}
LoadBalancerClientConfigurationRegistrar
LoadBalancerClientConfigurationRegistrar
是一个 ImportBeanDefinitionRegistrar
实现,用于根据 @LoadBalancerClient
注解配置负载均衡客户端,并将相应的客户端配置注册到 Spring 上下文中。
public class LoadBalancerClientConfigurationRegistrar implements ImportBeanDefinitionRegistrar {private static String getClientName(Map<String, Object> client) {if (client == null) {return null;}String value = (String) client.get("value");if (!StringUtils.hasText(value)) {value = (String) client.get("name");}if (StringUtils.hasText(value)) {return value;}throw new IllegalStateException("Either 'name' or 'value' must be provided in @LoadBalancerClient");}private static void registerClientConfiguration(BeanDefinitionRegistry registry, Object name, Object configuration) {BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(LoadBalancerClientSpecification.class);builder.addConstructorArgValue(name);builder.addConstructorArgValue(configuration);registry.registerBeanDefinition(name + ".LoadBalancerClientSpecification", builder.getBeanDefinition());}@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {Map<String, Object> attrs = metadata.getAnnotationAttributes(LoadBalancerClients.class.getName());if (attrs != null && attrs.containsKey("value")) {AnnotationAttributes[] clients = (AnnotationAttributes[]) attrs.get("value");for (AnnotationAttributes client : clients) {registerClientConfiguration(registry, getClientName(client), client.get("configuration"));}}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"));}Map<String, Object> client = metadata.getAnnotationAttributes(LoadBalancerClient.class.getName());String name = getClientName(client);if (name != null) {registerClientConfiguration(registry, name, client.get("configuration"));}}}
LoadBalancerClientFactory
LoadBalancerClientFactory
是一个工厂类,用于根据不同服务 ID 创建子容器,管理客户端和负载均衡器的实例。
public class LoadBalancerClientFactory extends NamedContextFactory<LoadBalancerClientSpecification> implements ReactiveLoadBalancer.Factory<ServiceInstance> {private static final Log log = LogFactory.getLog(LoadBalancerClientFactory.class);public static final String NAMESPACE = "loadbalancer";public static final String PROPERTY_NAME = NAMESPACE + ".client.name";private final LoadBalancerClientsProperties properties;public LoadBalancerClientFactory(LoadBalancerClientsProperties properties) {super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME, new HashMap<>());this.properties = properties;}public LoadBalancerClientFactory(LoadBalancerClientsProperties properties, Map<String, ApplicationContextInitializer<GenericApplicationContext>> applicationContextInitializers) {super(LoadBalancerClientConfiguration.class, NAMESPACE, PROPERTY_NAME, applicationContextInitializers);this.properties = properties;}public static String getName(Environment environment) {return environment.getProperty(PROPERTY_NAME);}@Overridepublic ReactiveLoadBalancer<ServiceInstance> getInstance(String serviceId) {return getInstance(serviceId, ReactorServiceInstanceLoadBalancer.class);}@Overridepublic LoadBalancerProperties getProperties(String serviceId) {if (properties == null) {if (log.isWarnEnabled()) {log.warn("LoadBalancerClientsProperties is null. Please use the new constructor.");}return null;}if (serviceId == null || !properties.getClients().containsKey(serviceId)) {// no specific client properties, return defaultreturn properties;}// because specifics are overlayed on top of defaults, everything in `properties`,// unless overridden, is in `clientsProperties`return properties.getClients().get(serviceId);}@SuppressWarnings("unchecked")public LoadBalancerClientFactory withApplicationContextInitializers(Map<String, Object> applicationContextInitializers) {Map<String, ApplicationContextInitializer<GenericApplicationContext>> convertedInitializers = new HashMap<>();applicationContextInitializers.keySet().forEach(contextId -> convertedInitializers.put(contextId,(ApplicationContextInitializer<GenericApplicationContext>) applicationContextInitializers.get(contextId)));return new LoadBalancerClientFactory(properties, convertedInitializers);}}
LoadBalancerAutoConfiguration
LoadBalancerAutoConfiguration
是 Spring Cloud LoadBalancer 的自动配置类,它提供了负载均衡相关的默认 Bean 配置,包括 LoadBalancerClientFactory
、LoadBalancerZoneConfig
和 LoadBalancerEagerContextInitializer
,并根据配置条件自动启用负载均衡功能。
@Configuration(proxyBeanMethods = false)
@LoadBalancerClients
@EnableConfigurationProperties({ LoadBalancerClientsProperties.class, LoadBalancerEagerLoadProperties.class })
@AutoConfigureBefore({ ReactorLoadBalancerClientAutoConfiguration.class, LoadBalancerBeanPostProcessorAutoConfiguration.class })
@ConditionalOnProperty(value = "spring.cloud.loadbalancer.enabled", havingValue = "true", matchIfMissing = true)
public class LoadBalancerAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic LoadBalancerZoneConfig zoneConfig(Environment environment) {return new LoadBalancerZoneConfig(environment.getProperty("spring.cloud.loadbalancer.zone"));}@ConditionalOnMissingBean@Beanpublic LoadBalancerClientFactory loadBalancerClientFactory(LoadBalancerClientsProperties properties, ObjectProvider<List<LoadBalancerClientSpecification>> configurations) {LoadBalancerClientFactory clientFactory = new LoadBalancerClientFactory(properties);clientFactory.setConfigurations(configurations.getIfAvailable(Collections::emptyList));return clientFactory;}@Beanpublic LoadBalancerEagerContextInitializer loadBalancerEagerContextInitializer(LoadBalancerClientFactory clientFactory, LoadBalancerEagerLoadProperties properties) {return new LoadBalancerEagerContextInitializer(clientFactory, properties.getClients());}@Beanstatic LoadBalancerChildContextInitializer loadBalancerChildContextInitializer(LoadBalancerClientFactory loadBalancerClientFactory, ApplicationContext parentContext) {return new LoadBalancerChildContextInitializer(loadBalancerClientFactory, parentContext);}}
实战
package xyz.idoly.demo;import java.util.Arrays;
import java.util.List;import org.springframework.cloud.client.DefaultServiceInstance;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.loadbalancer.annotation.LoadBalancerClient;
import org.springframework.cloud.loadbalancer.core.RandomLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ReactorLoadBalancer;
import org.springframework.cloud.loadbalancer.core.ServiceInstanceListSupplier;
import org.springframework.cloud.loadbalancer.support.LoadBalancerClientFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.web.client.RestClient;import reactor.core.publisher.Flux;@Configuration
@LoadBalancerClient(value = "user-services", configuration = UserServicesLoadBalancerConfiguration.class)
public class RestClientConfig {// 使用 @LoadBalanced 创建 RestClient.Builder@Bean@LoadBalancedpublic RestClient.Builder loadBalancerRestClientBuilder() {return RestClient.builder();}
}class UserServicesLoadBalancerConfiguration {// 1. 创建自定义的 ReactorLoadBalancer 实现// @Bean// @Primary// ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) {// String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME);// return new RandomLoadBalancer(loadBalancerClientFactory.getLazyProvider(name, ServiceInstanceListSupplier.class),name// );// }// 2. 创建 ServiceInstanceListSupplier,用于提供服务实例列表@Beanpublic ServiceInstanceListSupplier serviceInstanceListSupplier(Environment environment) {// 返回自定义的服务实例列表提供者return new UserServicesServiceInstanceListSupplier("user-services");}// 3. 自定义的 ServiceInstanceListSupplier 实现static class UserServicesServiceInstanceListSupplier implements ServiceInstanceListSupplier {private final String serviceId;UserServicesServiceInstanceListSupplier(String serviceId) {this.serviceId = serviceId;}@Overridepublic String getServiceId() {return serviceId;}@Overridepublic Flux<List<ServiceInstance>> get() {// 模拟返回多个服务实例return Flux.just(Arrays.asList(new DefaultServiceInstance(serviceId + "1", serviceId, "localhost", 8090, false),new DefaultServiceInstance(serviceId + "2", serviceId, "localhost", 9090, false),new DefaultServiceInstance(serviceId + "3", serviceId, "localhost", 9091, false)));}}
}