客户端负载均衡 Spring Cloud Ribbon
Spring Cloud Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,它基于Netflix Ribbon实现,可以将面向服务的REST模板请求自动转换为客户端负载均衡的服务调用。
1. 客户端负载均衡
负载均衡在系统架构中是一个非常重要且不得不去实施的内容。负载均衡是对系统的高可用,网络压力的缓解和处理能力的扩容的重要手段之一。通常所说的负载均衡是指服务端负载均衡,而客户端负载均衡和服务端负载均衡最大的不同点在于服务清单所存储的位置。在客户端负载均衡中,所有的客户端节点都维护着自己要访问的服务端清单,也需要心跳去维护服务端清单的健康性,只是需要与注册中心配合完成。
1.1 使用客户端负载均衡调用
- 服务提供者启动多个服务实例并注册到一个或多个相关联的服务注册中心
@Bean
@LoadBelanced
RestTemplate restTemplate() {
return new RestTemplate();
}
- 服务消费者通过调用被
@LoadBalanced
注解修饰的RestTemplate来实现面向服务的接口调用
@Autowired
private RestTemplate restTemplate;
@RequestMapping(value = "/ribbon-consumer", method = RequestMethod.GET)
public String helloConsumer() {
return restTemplate.getForEntity("http://HELLO-SERVICE/hello", String.class).getBody();
}
1.2 RestTemplate详解
RestTemplate对象会使用Ribbon的自动化配置,同时通过配置@LoadBalanced
开启客户端的负载均衡。
RestTemplate针对不同请求类型和参数类型的服务调用实现请参见JDK文档。
2. 负载均衡策略及配置
2.1 负载均衡策略
在Ribbon中实现了非常多的选择策略(IRule接口的各个实现)
- RandomRule:随机选择策略
该策略实现了从服务实例清单中随机选择一个服务实例,具体的选择逻辑在一个while(server == null)循环之内,若出现死循环获取不到服务实例时,很有可能存在并发的bug。 - RoundRobinRule:线性轮询选择策略
该策略实现了按照线性轮询的方式依次选择每个服务实例,与随机策略除获取实例逻辑不同外,在循环条件中新增一个计数器,当一直获取不到实例超过10次就会结束尝试。而线性轮询的实现是通过AtomicInteger nextServerCyclicCounter
对象实现,每次进行实例选择时调用incrementAndGetModulo
函数实现递增。 - RetryRule:具备重试机制的选择策略
该策略实现了一个具备重试机制的实例选择,在其内部定义了一个IRule对象,默认使用了RoundRobinRule选择策略。实现了对内部选择策略进行反复尝试的策略,若期间能选择到具体服务实例就返回,否则根据根据设置的尝试结束时间为阈值(maxRetryMills参数定义的值 + choose方法开始执行的时间戳),当超过该阈值返回null。 - WeightResponseTimeRule:加权选择策略
该策略是对RoundRobinRule的扩展,增加了根据实例的运行情况来计算权重,并根据权重来挑选实例,它的实现主要有三个核心内容。- 定时任务:WeightResponseTimeRule策略在初始化的时候会启动一个定时任务,用来为每个服务实例计算权重,该任务默认每30秒执行一次
- 权重计算:通过maintainWeights函数实现。先根据LoadBalancerStats记录每个实例的统计信息,累加所有实例的平均响应时间得到总平均响应时间totalResponseTime,再为实例清单逐个计算权重,规则为 weightSoFar + totalResponseTime - 实例平均响应时间,其中weightSoFar初始化为0,且每计算好几个权重需要累加到weightSoFar上供下一次计算使用。需要注意的是权重值只是表示各实例的权重区间,并非每个实例的优先级,因此不是数值越大选中概率越大。
- 实例选择:生成[0, 最大权重值)区间的随机数,遍历权重列表,若权重值大于等于随机数则用当前权重列表的索引获取服务实例。
- BestAvailableRule:最空闲连接
该策略利用LoadBalancerStats中保存的实例统计信息,通过遍历负载均衡器中维护的所有服务实例,过滤故障的实例并找出请求数最小的实例。
2.2 负载均衡配置
2.2.1 自动化配置
在引入Spring Cloud Ribbon依赖后,构建以下接口的实现
interface | description | default |
---|---|---|
IClientConfig | Ribbon客户端配置 | com.netflix.client.config.DefaultClientConfigImpl |
IRule | Ribbon负载均衡策略 | com.netflix.loadbalancer.ZoneAvoidanceRule |
IPing | Ribbon实例检查策略 | com.netflix.loadbalancer.NoOpPing |
ServerList | 服务实例清单维护机制 | com.netflixloadbalancer.ConfigurationBasedServerList |
ServerListFilter | 服务实例清单过滤机制 | org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter |
ILoadBalancer | 负载均衡器 | com.netflix.loadbalancer.ZoneAwareLoadBalancer |
修改Ribbon实例检查策略示例
@Configuration
public class MyRibbonConfiguration {
@Bean
public IPing ribbonPing(IClientConfig config) {
return new PingUrl();
}
}
2.2.2 Camden版本对RibbonClient配置优化
在Camden版本中,Spring Cloud Ribbon对RibbonClient定义个性化配置的方法做了进一步优化。可以直接通过
key | description |
---|---|
NFLoadBalancerPingClassName | 配置IPing接口的实现 |
NFLoadBalancerClassName | 配置ILoadBalancer接口的实现 |
NFLoadBalancerRuleClassName | 配置IRule接口的实现 |
NIWSServerListClassName | 配置ServerList接口的实现 |
NIWSServerListFilterClassName | 配置ServerListFilter接口的实现 |
修改Ribbon实例检查策略示例
服务名.ribbon.NFLoadBalancerPingClassName=com.netflix.loadbalancer.PingUrl
2.2.3 与Eureka结合
当在Spring Cloud的应用中同时引入Spring Cloud Ribbon和Spring Cloud Eureka依赖时,会触发Eureka中实现的对Ribbon的自动化配置。在与Spring Cloud Eureka结合使用时,配置将更加简单,不在需要需要通过类似
.ribbon. = 的参数指定具体的服务实例清单。而对于Ribbon的参数配置,依然可使用2.2.2中的两种配置方式,对于客户端的配置方式可直接使用Eureka中的服务名作为 完成针对某个微服务的个性配置。
Spring Cloud Ribbon默认实现了区域亲和策略,可以通过Eureka实例的元数据配置来实现区域化的实例配置方案。
如:eureka.instance.metadataMap.zone=shanghai
在Spring Cloud Ribbon和Spring Cloud Eureka结合的工程中,可以通过参数配置的方式禁用Eureka对Ribbon服务实例的维护实现。
如:ribbon.eureka.enabled=false