Ribbon策略描述
@LoadBalance注解详解
要想了解ribbon负载均衡,首先要了解LoadBalance的注入方式。了解@LoadBalance怎么把RestTemplate注入容器的。
/**
* Annotation to mark a RestTemplate or WebClient bean to be configured to use a
* LoadBalancerClient.
* @author Spencer Gibb
*/
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
@Target 注解:表示注解的作用范围,取值如下:
public enum ElementType {
TYPE, # 类、接口(包括注释类型)或枚举声明
FIELD, # 字段声明(包括enum常量)
METHOD, # 方法声明
PARAMETER, # 正式的参数声明
CONSTRUCTOR, # 构造函数声明
LOCAL_VARIABLE, # 局部变量说明
ANNOTATION_TYPE, # 注解类型声明
PACKAGE, # 程序包说明 /包说明
TYPE_PARAMETER, # 类型参数声明
TYPE_USE # 一种类型的使用
}
@Retention 注解,用来表示注解的生命周期,取值如下:
public enum RetentionPolicy {
SOURCE, # 只在源文件中起作用,编译的时候就没有了,一般用于检查类的,例如:@Override
CLASS, # 默认值,作用于class文件,用于在一些预编译处理,不常用。
RUNTIME # 作用于运行时期,能在运行时动态调用注解。
}
@Documented 注解:
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Qualifier
public @interface LoadBalanced {
}
当有@Documented生成的api文档时会显示上面的注解,如下:
@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Qualifier
public @interface LoadBalanced
当没有有@Documented生成的api文档时会显示上面的注解,如下:
public @interface LoadBalanced
@Inherited 注解:
- @Inherited只能用于注解类上,修饰在其他地方不起作用。
- 被@Inherited表示的注解,自动继承。
说明:@Inherited标识的注解@Test,修饰在一个类上,那么这个类的子类将自动继承注解@Test。但是@Test修饰在一个接口类上,那么这个接口的子类,或者实现类并不会继承注解@Test。
@Qualifier 注解:
在类注入的时候给类取一个标识,比如一个接口有多个实现类的时候,如果我们用@Autowrite(根据类型注入),就找不到具体的实现类,这时候可以添加@Qualifier(value=“**”)来标识你具体注入的实现类。
上面是一种使用情况,还有一个对要自动装配的类做标识,例如:
@Bean
public Date date1(){
return new Date();
}
@Bean
public Date date2(){
return new Date();
}
@Autowired(required = false)
public List<Date> list = Collections.emptyList();
这时候date1和date2都会被注入到list中,如果添加@Qualifier注解,就只会注入有@Qualifier标识的Bean。
@Bean
public Date date1(){
return new Date();
}
@Qualifier
@Bean
public Date date2(){
return new Date();
}
@Qualifier
@Autowired(required = false)
public List<Date> list = Collections.emptyList();
在使用Ribbon做负载均衡时注入RestTemplate
在ribbon做负载均衡时,需要在配置类注入RestTemplate:
@Bean
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
在配置类:org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration中维护了一个RestTemplate的集合
@LoadBalanced
@Autowired(required = false)
private List<RestTemplate> restTemplates = Collections.emptyList();
因为@Loadbalanced用了@Qualifier注解,所有所有被@Loadbalanced修饰的RestTemplate都会被注入到restTemplates 之中。
ribbon负载均衡策略
ribbon负载均衡内置的实现方式。
RoundRobinRule: 轮询策略
WeightedResponseTimeRule: 权重策略
根据每个服务提供者的响应时间分配一个权重,响应时间越长权重越小,被选中的可能性也就越低。 它的实现原理是,刚开始使用轮询策略并开启一个计时器,每一段时间收集一次所有服务提供者的平均响应时间,然后再给每个服务提供者附上一个权重,权重越高被选中的概率也越大。
RandomRule: 随机策略
BestAvailableRule: 最小连接数策略
也叫最小并发数策略,它是遍历服务提供者列表,选取连接数最小的⼀个服务实例。如果有相同的最小连接数,那么会调用轮询策略进行选取
RandomRule: 重试策略
按照轮询策略来获取服务,如果获取的服务实例为null或已经失效,则在指定的时间之内不断地进行重试来获取服务,如果超过指定时间依然没获取到服务实例则返回null
AvailabilityFilteringRule: 可用性敏感策略
先过滤掉非健康的服务实例,然后再选择连接数较小的服务实例
ZoneAvoidanceRule: 区域敏感策略
根据服务所在区域(zone)的性能和服务的可用性来选择服务实例,在没有区域的环境下,该策略和轮询策略类似。
@Bean注入的方式,全局配置
在配置类中注入负载均衡策略,这样是全局的所有的服务都会用这个策略。
@Bean
public IRule iRule(){
return new RandomRule();
}
通过配置文件配置负载均衡策略
可以指定服务使用负载策略
user-service: # 服务名称
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
如何通过@Loadbalanced实现负载均衡
1、将RestTemplate注入LoadBalancerAutoConfiguration配置类维护的List< RestTemplate>
2、为RestTemplate的添加LoadBalancerInterceptor拦截器。
@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);
}
}
});
}
3、LoadBalancerInterceptor会拦截RestTemplate发出的请求,因为LoadBalancerInterceptor实现了ClientHttpRequestInterceptor接口。
@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);
return this.loadBalancer.execute(serviceName,
this.requestFactory.createRequest(request, body, execution));
}
这里this.loadBalancer.execute的实现类,RibbonLoadBalancerClient的execute方法,里面的getServer方法实现的负载均衡。