通常所说的负载均衡,一般来说都是在服务器端使用 Ngnix 或 F5 做 Server 的负载均衡策略,在 Ribbon 中提到的负载均衡,一般来说是指的客户端负载均衡
,即 ServiceA 调用 ServiceB,有多个 ServiceB 的情况下,由 ServiceA 选择调用哪个 ServiceB。
负载均衡与 Ribbon 负载均衡(Load Balance),是一种利用特定方式,将流量分摊到多个操作单元上的手段,它对系统吞吐量、系统处理能力有着质的提升。最常见的负载均衡分类方式有:软负载、硬负载,对应 Ngnix、F5;集中式负载均衡、进程内负载均衡。集中式负载均衡是指位于网络和服务提供者之间,并负责把忘了请求转发到各个提供单位,代表产品有 Ngnix、F5;进程负载均衡是指从一个实例库选取一个实例进行流量导入,在微服务范畴,实例库一般是存储在 Eureka、Consul、Zookeeper 等注册中心,此时的负载均衡器类似 Ribbon 的 IPC(进程间通信)组件,因此进程内负载均衡也叫做客户端负载均衡。
Ribbon 是一个客户端负载均衡器,赋予了应用一些支配 HTTP 与 TCP 行为的能力,由此可以得知,这里的客户端负载均衡也是进程内负载均衡的一周。 Ribbon 在 SpringCloud 生态内的不可缺少的组件,没有了 Ribbon,服务就不能横向扩展。Feign、Zuul 已经集成了 Ribbon。
示例 Eureka Server 不再赘述,可以直接使用 spring-cloud-eureka-server-simple
。
Consumer 源码:https://gitee.com/laiyy0728/spring-cloud/tree/master/spring-cloud-ribbon/spring-cloud-ribbon-consumer
yml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 spring: application: name: spring-cloud-ribbon-consumer server: port: 9999 eureka: client: service-url: defaultZone: http://localhost:8761/eureka instance: prefer-ip-address: true instance-id: ${spring.application.name}:${server.port}
配置类:
1 2 3 4 5 @Bean @LoadBalanced public RestTemplate restTemplate () { return new RestTemplate(); }
@LoadBalanced
:对 RestTemplate 启动负载均衡
Consumer Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @RestController public class ConsumerController { private final RestTemplate restTemplate; @Autowired public ConsumerController (RestTemplate restTemplate) { this .restTemplate = restTemplate; } @GetMapping (value = "/check" ) public String checkRibbonProvider () { return restTemplate.getForObject("http://spring-cloud-ribbon-provider/check" , String.class); } }
provider 源码:https://gitee.com/laiyy0728/spring-cloud/tree/master/spring-cloud-ribbon/spring-cloud-ribbon-provider
pom 依赖:
1 2 3 4 5 6 7 8 9 10 11 <dependencies > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-netflix-eureka-client</artifactId > </dependency > <dependency > <groupId > org.springframework.cloud</groupId > <artifactId > spring-cloud-starter-netflix-ribbon</artifactId > </dependency > </dependencies >
配置文件:
1 2 3 4 5 6 7 8 9 10 11 spring: application: name: spring-cloud-ribbon-provider eureka: client: service-url: defaultZone: http://localhost:8761/eureka instance: prefer-ip-address: true instance-id: ${spring.application.name}:${server.port}
ProviderContr
1 2 3 4 5 6 7 8 9 10 11 @RestController public class ProviderController { @Value ("${server.port}" ) private int port; @GetMapping (value = "/check" ) public String providerPort () { return "Provider Port: " + port; } }
验证 分别启动 Eureka Server、Consumer、Provider,其中,Provider 以 mvn 形式启动,绑定不同的端口号:
1 2 mvn spring-boot:run -Dserver.port=8080 mvn spring-boot:run -Dserver.port=8081
postman 访问 Consumer
可以看到,Provider 两次返回值不一样,验证了负载均衡成功。
负载均衡策略 Ribbon 中提供了 七种
负载均衡策略
策略类 命名 描述 RandomRule 随机策略 随机选择 Server RoundRobinRule 轮询策略 按照顺序循环选择 Server RetryRule 重试策略 在一个配置时间段内,当选择的 Server 不成功,则一直尝试选择一个可用的 Server BestAvailableRule 最低并发策略 逐个考察 Server,如果 Server 的断路器被打开,则忽略,在不被忽略的 Server 中选择并发连接最低的 Server AvailabilityFilteringRule 可用过滤测试 过滤掉一直连接失败,并被标记未 circuit tripped(即不可用) 的 Server,过滤掉高并发的 Server ResponseTimeWeightedRule 响应时间加权策略 根据 Server 的响应时间分配权重,响应时间越长,权重越低,被选择到的几率就越低 ZoneAvoidanceRule 区域权衡策略 综合判断 Server 所在区域的性能和 Server 的可用性轮询选择 Server,并判定一个 AWS Zone 的运行性能是否可用,剔除不可用的 Zone 中的所有 Server
Ribbon 默认的负载均衡策略是 轮询策略
。
设置负载均衡策略 设置全局负载均衡 创建一个声明式配置,即可实现全局负载均衡配置:
1 2 3 4 5 6 7 8 9 10 11 @Configuration public class RibbonConfig { @Bean public IRule ribbonRule () { return new RandomRule(); } }
重启 Consumer,访问测试
基于注解的配置 空注解 声明一个空注解,用于使用注解配置 Ribbon 负载均衡
1 2 public @interface RibbonAnnotation {}
负载均衡配置类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @Configuration @RibbonAnnotation public class RibbonAnnoConfig { private final IClientConfig clientConfig; @Autowired (required = false ) public RibbonAnnoConfig (IClientConfig clientConfig) { this .clientConfig = clientConfig; } @Bean public IRule ribbonRule (IClientConfig clientConfig) { return new RandomRule(); } }
启动类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 @SpringBootApplication @EnableDiscoveryClient @RibbonClient (name = "spring-cloud-ribbon-provider" , configuration = RibbonAnnoConfig.class)@ComponentScan (excludeFilters = {@ComponentScan .Filter(type = FilterType.ANNOTATION, value = RibbonAnnotation.class)})public class SpringCloudRibbonConsumerApplication { public static void main (String[] args) { SpringApplication.run(SpringCloudRibbonConsumerApplication.class, args); } @Bean @LoadBalanced public RestTemplate restTemplate () { return new RestTemplate(); } }
@RibbonClient
:针对 spring-cloud-ribbon-provider
服务,使用负载均衡,配置类是 configuration
标注的类。@ComponentScan
:让 Spring 不去扫描被 @RibbonAnnotation
类标记的配置类,因为我们的配置对单个服务生效,不能应用于全局,如果不排除,启动就会报错
如果需要对多个服务进行配置,可以使用 @RibbonClients
注解
1 2 3 @RibbonClients (value = { @RibbonClient (name = "spring-cloud-ribbon-provider" , configuration = RibbonAnnoConfig.class) })
重启 Consumer,验证基于注解的负载均衡是否成功
基于配置文件的负载均衡策略 语法:
1 2 3 {instance-id}: ribbon: NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule
Ribbon 配置 源码:https://gitee.com/laiyy0728/spring-cloud/tree/master/spring-cloud-ribbon/spring-cloud-ribbon-config
超时与重试 HTTP 请求难免会出现请求超时,此时对调用进行时限的控制以及在时限之后的重试尤为重要。对于超时重试的配置如下:
1 2 3 4 5 6 7 {instance-id}: ribbon: ConnectTimeout: 30000 ReadTimeout: 30000 MaxAutoRetries: 1 MaxAutoRetriesNextServer: 1 OkToRetryOnAllOperations: true
Ribbon 饥饿加载 Ribbon 在进行负载均衡时,并不是启动时就加载上线文,而是在实际的请求发送时,才去请求上下文信息,获取被调用者的 ip、端口,这种方式在网络环境较差时,往往会使得第一次引起超时,导致调用失败。此时需要指定 Ribbon 客户端,进行饥饿加载
,即:在启动时就加载好上下文。
1 2 3 4 ribbon: eager-load: enabled: true clients: spring-cloid-ribbon-provider
此时启动 consumer,会看到控制打印信息如下:
1 2 3 Client: spring-cloid-ribbon-provider instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=spring-cloid-ribbon-provider,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null Using serverListUpdater PollingServerListUpdater DynamicServerListLoadBalancer for client spring-cloid-ribbon-provider initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=spring-cloid-ribbon-provider,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:org.springframework.cloud.netflix.ribbon.eureka.DomainExtractingServerList@79e7188e
可以看到启动时就加载了 spring-cloid-ribbon-provider
,并绑定了LoadBalancer
Ribbon 常用配置 配置项 说明 {instance-id}:ribbon.NFLoadBalancerClassName 指负载均衡器类路径 {instance-id}:ribbon:NFLoadBalancerRuleClassName 指定负载均衡算法类路径 {instance-id}:ribbom:NFLoadBalancerPingClassName 指定检测服务存活的类路径 {instance-id}:ribbon:NIWSServerListClassName 指定获取服务列表的实现类路径 {instance-id}:ribbon:NIWSServerListFilterClassName 指定服务的 Filter 实现类路径
Ribbon 脱离 Eureka 默认情况下,Ribbon 客户端会从 Eureka Server 读取服务注册信息列表,达到动态负载均衡的功能。如果 Eureka 是一个提供多人使用的公共注册中心(如 SpringCloud 中文社区公益 Eureka:http://eureka.springcloud.cn),此时极易产生服务侵入问题,此时就不能从 Eureka 中读取服务列表,而应该在 Ribbon 客户端自行制定源服务地址
1 2 3 4 5 6 7 ribbon: eureka: enabled: false {instance-id}: ribbon: listOfServers: http://localhost:8888