客户端负载均衡 spring cloud ribbon
- Ribbon是一个基于HTTP和TCP的客户端负载均衡工具,基于Netflix Ribbon实现
客户端负载均衡
- 服务端负载均衡
- 硬件负载均衡:通过在服务器节点安装专门用于负载均衡的设备
- 软件负载均衡:通过在服务器上安装一些具有均衡负载功能或模块的软件来完成请求分发工作
- 客户端负载均衡和服务端负载均衡最大的不同点是服务清单所存储的位置
- 客户端负载均衡:客户端节点维护着自己要访问的服务端清单,这些服务清单来自于服务注册中心。客户端负载均衡中也需要心跳去维护服务端清单的健康性,但需要与服务注册中心配合完成。
- 客户端负载均衡步骤:
- 服务提供者只需要启动多个服务实例并注册到一个注册中心或是多个相关联的服务注册中心
- 服务消费者直接通过调用被@LoadBalanced注解修饰过的RestTemplate来实现面向服务的接口调用
RestTemplate详解
GET请求
- getForEntity:返回ResponseEntity,对象是spring对http请求响应的封装,主要存储了http的几个重要元素。有三种不同重载实现
- getForObject:getForEntity的进一步封装,通过HttpMessageConverterExtractor对http的请求响应体body内容进行对象转换,实现请求直接返回包装好的对象内容,提供三种重载实现
POST请求
- postForEntity函数:调用后返回ReponseEntity,三种重载方法
- postForObject函数:作用是简化postForEntity的后续处理,通过直接将请求响应的body内容包装成对象来返回使用
- postForLocation函数:实现了以post请求提交资源,并返回新资源的uri
PUT请求
- 三种不同重载方法
- 函数为void类型,所以没有返回内容,也没有其他函数定义的responseType参数
DELETE请求
- 三种不同重载方法
- 在rest请求时,将delete请求的唯一标识拼接在url中,delete请求不需要request的body信息,url指定delete请求位置,URLVariables绑定url中的参数即可。
源码分析
负载均衡器
ILoadBalancer接口的实现类实现客户端负载均衡
负载均衡策略
IRule接口的各个实现
- AbstractLoadBalancerRule
- 负载均衡抽象类,定义了负载均衡器ILoadBalancer对象,该对象能够在具体实现选择服务策略时,获取到一些负载均衡器中维护的信息来作为分配依据,并设计一些算法来实现针对特定场景的高效策略
- RandomRule
- 从服务实例清单中随机选一个服务实例
- choose具体实现:
- 会使用传入的负载均衡器来获得可用实例列表uplist和所有实例列表allList
- 通过rand.nextInt函数来获取一个随机数
- 将该随机数作为uplist的索引值来返回具体实例
- 具体的选择逻辑在一个while循环内,根据选择逻辑的实现,正常情况下每次选择出一个服务实例,如果出现死循环获取不到服务实例时,很有可能存在并发bug
- RoundRobinRule
- 实现了按照线性轮询的方式一次选择每个服务实例的功能
- 具体实现:线性轮询增加了一个count计数变量,变量会在每次循环之后累加,即如果一直选择不到server超过10次,就会结束尝试,并打印警告信息,线性轮询的实现则是通过AtomicInteger实现,每次进行实例选择时通过调用incrementAndGetModulo函数实现递增。
- RetryRule
- 实现了一个具备重试机制的实例选择功能。
- 默认使用RoundRobinRule实例,而在choose方法中则实现了对内部定义的策略进行反复尝试的策略,若期间能够选择到具体的服务实例就返回,若选择不到就根据设置的尝试结束时间为阈值,当超过该阈值后就返回null
- WeightedResponseTimeRule
- 对RoundRobinRule的扩展,增加了根据实例的运行情况来计算权重,并根据权重来挑选实例,达到最优的分配效果
- 定时任务
- 初始化时会通过serverWeightTimer.schedule(…)启动一个定时任务,用来为每个服务实例计算权重,该任务默认30秒执行一次
- 权重计算
- 用于存储权重的对象List 该list中每个权重值所处的位置对应了负载均衡器维护的服务实例清单中所有实例在清单中的位置。
- 维护实例权重计算过程maintainWeights
- 根据LoadBalanceerStats中记录的每个实例的统计信息吗,累加所有实例的平均响应时间,得到总平均响应时间totalResponseTime,该值会用于后续的计算。
- 为负载均衡器中维护的实例清单逐个计算权重,计算规则为weightSoFar+totalResponseTime-实例的平均响应时间,
- 这里的权重值只是表示了各实例权重区间的上限,并非某个实例的优先级,所以不是数值越大被选中的概率就越大
- 实例选择
- 生成一个[0,最大权重值)区间内的随机数
- 遍历权重列表,比较权重值与随机数的大小,如果权重值大于等于随机数 ,就拿当前权重列表的索引值去服务实例列表中获取具体的实例,
- ClientConfigEnabledRoundRobinRule
- 内部定义了一个RoundRobinRule策略,choose函数使用了RoundRobinRule线性轮询机制
- BestAvailableRule
- 选出最空闲的实例
- 继承自ClientConfigEnabledRoundRobinRule,在实现中它注入了负载均衡器的统计对象LoadBalancerStats,在choose中利用LoadBalancerStats保存的实例统计信息来选择满足要求的实例
- 通过遍历负载均衡器中维护的所有服务实例,会过滤掉故障的实例,并找出并发请求数最小的一个
- PredicateBasedRule
- 先通过子类中实现的Predicate逻辑来过滤一部分服务实例
- 以线性轮询的方式从过滤后的实例清单中选出一个
- 过滤功能getEligibleServers函数
- 遍历服务清单,使用this.apply方法来判断实例是否需要保留,如果是就添加到结果列表中
- AvailabilityFilteringRule
- 继承了先过滤清单,再轮询选择的基本处理逻辑,过滤条件使用了AvailabilityPredicate
- 过滤逻辑shouldSkipServer。只满足下面一项apply就返回false,都不满足就返回true
- 是否故障,即断路器是否生效已断开
- 实例的并发请求数大于阈值
- 以线性方式选择一个实例,接着用过滤条件来判断该实例是否满足要求,如此循环,10次如果没有找到符合要求的实例,就采用父类的实现方案。
- 该策略通过线性抽样的方式直接尝试寻找可用且较空闲的实例来使用。
- ZoneAvoidanceRule
- 完全遵循了父类的过滤主逻辑,先过滤清单,再轮询选择,
- 在获取过滤结果的实现函数getEligibleServers
- 使用主过滤条件对所有实例过滤并返回过滤后的实例清单
- 依次使用次过滤条件列表中的过滤条件对主过滤条件的结果进行过滤
- 每次过滤之后都需要判断,只要符合一个就不再进行过滤,将当前结果返回供线性轮询算法选择
- 过滤后的实例总数>=最小过滤实例数
- 过滤后的实例比例>最小过滤百分比
配置详解
自动化配置
在引入spring cloud ribbon依赖后,就能自动化构建接口实现:
- IClientConfig:Ribbon客户端配置,默认采用com.netflix.client.config.DefaultClientConfigImpl实现
- IRule:Ribbon负载均衡策略,默认采用com.netfix.loadbalancer.ZoneAvoidanceRule实现,该策略能够咋在多区域环境下选出最佳区域的实例进行访问
- IPing:Ribbon实例检查策略,默认采用com.netflix.loadbalancer.NoOpPing实现,该检查策略是一个特殊实现,实际上它并不会检查实例是否可用,而是始终返回true,默认认为所有服务实例都是可用的。
- ServerList:服务实例清单的维护机制,默认采用com.netflix.loadbalancer.ConfigurationBasedServerList实现。
- ServerListFilter:服务实例清单过滤机制,默认org.springframework.cloud.netflix.ribbon.ZonePreferenceServerListFilter实现,该策略能够优先过滤出与请求调用方处于同区域的服务实例
- ILoadBalancer:负载均衡器,默认采用com.netflix.loadbalancer.ZoneAwareLoadBalancer实现,具备区域感知能力
Camden版本对RibbonClient配置的优化
参数配置
对ribbon的参数配置
- 全局配置
- ribbon.=
- key:Ribbon客户端配置的参数名
- value:对应参数的值
- 指定客户端配置
- .ribbon.=
- key:Ribbon客户端配置的参数名;value:对应参数的值