简单示例
Ribbon的使用在Eureka - 简单示例已经提过了一下,我们可以把provider复制一份出来,修改端口并注册到注册中心,如下图所示(代码跟之前的雷同,就不贴了):
核心调用代码:
ResponseEntity forEntity = restTemplate.getForEntity("http://eureka-provider/getInfo?name=" + name, String.class);
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
在url里并没有写ip和端口,而是应用名称。访问Consumer的地址,可以看到轮询的访问两个Provider。
源码分析
@RibbonClients
还是从spring.factories说起,在Eureka Client和Ribbon的spring.factories有以下内容:
// Ribbon
org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration
// Eureka Client
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration
这两个类都有@RibbonClients
注解,里面import了RibbonClientConfigurationRegistrar类。
@Configuration(proxyBeanMethods = false)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
@Documented
@Import(RibbonClientConfigurationRegistrar.class)
public @interface RibbonClients {
RibbonClient[] value() default {};
Class[] defaultConfiguration() default {};
}
RibbonClientConfigurationRegistrar实现了ImportBeanDefinitionRegistrar接口,这个接口是用来动态的创建bean,registerBeanDefinitions方法里,会处理使用@RibbonClients
和 @RibbonClient
注解的类,并调用下面的registerClientConfiguration方法。由于上面两个类使用了@RibbonClients
注解,所以会创建两个RibbonClientSpecification类型的bean,名字分别为default.org.springframework.cloud.netflix.ribbon.RibbonAutoConfiguration
和default.org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration
。
// 其他代码略
private void registerClientConfiguration(BeanDefinitionRegistry registry, Object name,
Object configuration) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder
.genericBeanDefinition(RibbonClientSpecification.class);
builder.addConstructorArgValue(name);
builder.addConstructorArgValue(configuration);
registry.registerBeanDefinition(name + ".RibbonClientSpecification",
builder.getBeanDefinition());
}
SpringClientFactory
SpringClientFactory是创建RibbonClient负载均衡的工厂类,他会创建一个独立的ApplicationContext以及RibbonClient相关的bean,这个后面会继续讲解这个内容。在SpringClientFactory实例化的时候,会赋值configurations,这个configurations就是上面的RibbonClientSpecification。SpringClientFactory实例化的适合,在构造函数会传RibbonClientConfiguration.class,这个后面讲解作用。
@Bean
@ConditionalOnMissingBean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
LoadBalancerClient
客户端负载均衡器,这里注入的上面的SpringClientFactory。
@Bean
@ConditionalOnMissingBean(LoadBalancerClient.class)
public LoadBalancerClient loadBalancerClient() {
return new RibbonLoadBalancerClient(springClientFactory());
}
LoadBalancerRequestFactory
这里注入的上面的LoadBalancerClient。
@Bean
@ConditionalOnMissingBean
public LoadBalancerRequestFactory loadBalancerRequestFactory(
LoadBalancerClient loadBalancerClient) {
return new LoadBalancerRequestFactory(loadBalancerClient, this.transformers);
}
LoadBalancerInterceptor
主要是用于客户端请求的拦截,通过拦截实现负载均衡。这里注入的是LoadBalancerClient和LoadBalancerRequestFactory。
@Bean
public LoadBalancerInterceptor loadBalancerInterceptor(
LoadBalancerClient loadBalancerClient,
LoadBalancerRequestFactory requestFactory) {
return new LoadBalancerInterceptor(loadBalancerClient, requestFactory);
}
RestTemplateCustomizer
restTemplate.setInterceptors(list),是给restTemplate增加LoadBalancerInterceptor,这个LoadBalancerInterceptor是上面注入的。
@Bean
@ConditionalOnMissingBean
public RestTemplateCustomizer restTemplateCustomizer(
final LoadBalancerInterceptor loadBalancerInterceptor) {
return restTemplate -> {
List list = new ArrayList<>(
restTemplate.getInterceptors());
list.add(loadBalancerInterceptor);
restTemplate.setInterceptors(list);
};
}
loadBalancedRestTemplateInitializerDeprecated
先看看LoadBalanced注解,这里引入的是@Qualifier
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
在LoadBalancerAutoConfiguration
类中,有这一行代码,他也引入了@LoadBalanced
,我们之前Eureka - 简单示例,RestTemplate实例化的时候也是加@LoadBalanced
注解的。他这个意思是只要加了@LoadBalanced
注解的,都注入到restTemplates,所以我们如果没有在RestTemplate,就没办法注入到这里,也就没办法后续的拦截器注入。
@LoadBalanced
@Autowired(required = false)
private List restTemplates = Collections.emptyList();
在loadBalancedRestTemplateInitializerDeprecated中,由于是SmartInitializingSingleton类型的,所以会调用afterSingletonsInstantiated
方法,return的部分就是这个方法。在这个方法中,会遍历上面@LoadBalanced
的restTemplate,然后再一个个调用RestTemplateCustomizer的方法,实现拦截器注入。
@Bean
public SmartInitializingSingleton loadBalancedRestTemplateInitializerDeprecated(
final ObjectProvider> restTemplateCustomizers) {
return () -> restTemplateCustomizers.ifAvailable(customizers -> {
for (RestTemplate restTemplate : LoadBalancerAutoConfiguration.this.restTemplates) {
for (RestTemplateCustomizer customizer : customizers) {
customizer.customize(restTemplate);
}
}
});
}