一、客户端负载均衡介绍
在客户端负载均衡中,所有客户端节点都维护着自己要访问的服务端清单,而这些服务端端清单来自于服务注册中心。
- 服务提供者只需要启动多个服务实例并注册到一个注册中心或是多个相关联的服务注册中心。
- 服务消费者直接通过调用被@LoadBalanced注解修饰过的RestTemplate来实现面向服务的接口调用。
这样,我们就可以将服务提供者的高可用以及服务消费者的负载均衡调用一起实现了。
Ribbon工作时分两步:
- 选择Eureka Server,它优先选择在同一个Zone且负载较少的Server。
- 根据用户指定的策略,在从Server取到的服务注册列表中选择一个地址,其中Ribbon提供了多种策略,例如轮询round robin、随机Random、根据响应时间加权等。
二、如何使用Ribbon
基本使用
ribbon依赖(spring-cloud-starter-netflix-eureka-client中已经帮我们集成了负载均衡组件):
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId> </dependency>
在RestTemplate的配置方法上添加@LoadBalanced注解。(注意:在启动类上一定要添加@EnableDiscoveryClient或@EnableEurekaClient来发现服务)
(RestTemplate 是由 Spring Web 模块提供的工具类,与 SpringCloud 无关,是独立存在的。因 SpringCloud 对 RestTemplate 进行了一定的扩展,所以 RestTemplate 具备了负载均衡的功能)。
ribbon负载均衡策略
策略类 | 命名 | 描述 |
RandomRule | 随机策略 | 随机选择server |
RoundRobinRule | 轮询策略 | 按照顺序选择server(ribbon默认策略) |
RetryRule | 重试策略 | 在一个配置时间段内,当选择server不成功,则一直尝试选择一个可用的server |
BestAvailableRule | 最低并发策略 | 逐个考察server,如果server断路器打开,则忽略,再选择其中并发链接最低的server |
AvailabilityFilteringRule | 可用过滤策略 | 过滤掉一直失败并被标记为circuit tripped的server,过滤掉那些高并发链接的server(active connections超过配置的阈值) |
ResponseTimeWeightedRule | 响应时间加权重策略 | 根据server的响应时间分配权重,响应时间越长,权重越低,被选择到的概率也就越低。响应时间越短,权重越高,被选中的概率越高,这个策略很贴切,综合了各种因素,比如:网络,磁盘,io等,都直接影响响应时间 |
ZoneAvoidanceRule | 区域权重策略 | 综合判断server所在区域的性能,和server的可用性,轮询选择server并且判断一个AWS Zone的运行性能是否可用,剔除不可用的Zone中的所有server |
如果想要创建一个全局的负载策略,只需添加一个配置类,也可自己扩展,添加逻辑,如下:
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; @Configuration public class RibbonConfiguration { @Bean public IRule ribbonRule() { return new RandomRule(); } }
如果想要对某个服务源设置特有的策略,可以在工程启动类上添加@RibbonClient注解,当然,对应配置代码也需要调整:
/** * 自定义-标记注解 */ public @interface AvoidScan { }
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import com.netflix.client.config.IClientConfig; import com.netflix.loadbalancer.IRule; import com.netflix.loadbalancer.RandomRule; /** * Ribbon负载策略配置类,IClientConfig是针对客户端的管理配置器,配合@RibbonClient注解使用 */ @Configuration @AvoidScan public class RibbonConfiguration { @Autowired private IClientConfig config; @Bean public IRule ribbonRule(IClientConfig config) { return new RandomRule(); } }
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.ribbon.RibbonClient; import org.springframework.cloud.netflix.ribbon.RibbonClients; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.FilterType; import cn.springcloud.book.config.AvoidScan; import cn.springcloud.book.config.TestConfiguration; /** * 工程启动类 */ @SpringBootApplication @EnableDiscoveryClient @RibbonClient(name = "client-a", configuration = RibbonConfiguration.class)//表示针对client-a服务使用的负责策略是经过RibbonConfiguration配置类的。 //@RibbonClients(value = { // @RibbonClient(name = "client-a", configuration = RibbonConfiguration.class), // @RibbonClient(name = "client-b", configuration = RibbonConfiguration.class) //})//这种方式跟@RibbonClient类似,不过这个是针对多个服务进行策略指定。 @ComponentScan(excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, value = {AvoidScan.class})})//表示让工程在启动的时候,不让Spring扫描被@AvoidScan注解标记的类, //因为配置的是针对特殊服务的负载策略,不是全局的,如果不排除,启动就会报错。 public class RibbonLoadbalancerApplication { public static void main(String[] args) { SpringApplication.run(RibbonLoadbalancerApplication.class, args); } }
如果想使用配置文件的方式,进行配置负责策略,语法是 client name.ribbon.*,client name是我们自己给服务取的名字,即:spring.application.name设置的值。如下:
client-a: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule #针对client-a服务使用随机策略
自定义负载均衡策略
继承AbstractLoadBalancerRule,重写choose方法。
重试机制
ribbon的重试机制,默认是开启的,需要添加超时与重试的策略配置,如下:
client-a: ribbon: ConnectTimeout: 30000 ReadTimeout: 60000 MaxAutoRetries: 1 #对第一次请求的服务的重试次数 MaxAutoRetriesNextServer: 1 #要重试的下一个服务的最大数量(不包括第一个服务) OkToRetryOnAllOperations: true
这里配置的ConnectTimeout和ReadTimeout是当HTTP客户端使用的是HttpClient才生效,这个时间最终会被设置到HttpClient中。在设置的时候需要结合hystrix的超时时间来综合考虑,针对使用的场景,设置太小会导致很多请求失败,设置太大会导致熔断控制变差。
提供了7个核心接口:
接口 | 简述 | 默认实现 |
IClientConfig | 定义ribbon中管理配置的接口 | DefaultClientConfigImpl |
IRule | 定义ribbon中负载均衡策略的接口 | ZoneAvoidanceRule |
IPing | 定义定期ping服务,检查可用性的接口 | DummyPing |
ServerList<Server> | 定义获取服务列表方法的接口 | ConfigurationBasedServerList |
ServerListFilter<Server> | 定义特定场景下,获取服务列表的方法接口 | ZonePreferenceServerListFilter |
ILoadBalancer | 定义负载均衡选择服务的核心方法接口 | ZoneAwareLoadBalancer |
ServerListUpdater | 为DynamicServerListLoadBalancer定义动态更新服务列表的接口 | PollingServerListUpdater |
自定义客户端
Ribbon的加载策略是懒加载,即第一次请求的时候才加载对应上下文,正是这个原因,很多时候第一次调用显得很慢,甚至会超时,所以,可以通过指定ribbon具体服务名称来开启饿加载,即在工程启动的时候,加载所有的配置上下文。如下:
ribbon: eager-load: enabled: true clients: client-a, client-b, client-c
ribbon在1.2.0版本以后,可以使用配置文件来定制ribbon客户端,这种优先级最高,高于使用@RibbonClient注解指定配置。
配置项 | 说明 |
<clientName>.ribbon.NFLoadBalancerClassName | 指定ILoadBalancer的实现类 |
<clientName>.ribbon.NFLoadBalancerRuleClassName | 指定IRule的实现类 |
<clientName>.ribbon.NFLoadBalancerPingClassName | 指定IPing的实现类 |
<clinetName>.ribbon.NIWSServerListClassName | 指定ServerList的实现类 |
<clientName>.ribbon.NIWSServerListFilterClassName | 指定ServerListFilter的实现类 |
可以使用ribbon的自带实现类,也可以使用我们自定义的接口实现类,如下:
client-a: ribbon: NIWSServerListClass: com.netflix.loadbalancer.ConfigurationBasedServerList NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
默认情况下,ribbon客户端会从eureka注册中心获取服务注册列表,然后做负载均衡策略,但是如果想要ribbon客户端脱离eureka使用,可以为ribbon客户端指定服务的地址,如下操作:
ribbon.eureka.enabled=false #一定要关闭ribbon中的eureka功能 client.ribbon.listOfServers=http://localhost:7070,http://localhost:7171 #指定服务地址