一、概要
Ribbon是Netflix公司开源的一个负载均衡的项目官方网站,它是一个基于HTTP、TCP的客户端负载均衡。Ribbon客户端组件提供一系列完善的配置项如连接超时,重试等
负载均衡(Load Balance,简称LB)是一种服务器或网络设备的集群技术。负载均衡将特定的业务(网络服务、网络流量等)分担给多个服务器或网络设备,从而提高了业务处理能力,保证了业务的高可用性。
通过负载均衡将用户的请求分发到不同的服务器用来提高网站、应用、数据库或其他服务的性能以及可靠性。
按着实现方式不同:
-
负载均衡硬件 : Foundry、F5、Array等等,
-
负载均衡软件: Nginx、LVS、Haproxy等等
按着使用方式的不同 有分为:
-
服务器负载均衡(集中式负载均衡)
-
客服端负载均衡(进程内负载均衡)
服务器负载均衡(nginx)
即在客户端和服务端之间使用独立的负载均衡设施, 由该设施负责把访问请求通过某种策略转发至服务端。
[图片上传失败...(image-531c3b-1597979086167)]
客户端负载均衡
客户端负载均衡与服务端负载均衡的区别在于客户端要维护一份服务列表
-
在消费微服务中使用Ribbon实现负载均衡,Ribbon先从Nacos Server中获取服务列表。
-
Ribbon根据负载均衡的算法去调用微服务。
二、Ribbon负载均衡策略
1、说明
Ribbon内置了非常丰富的负载均衡策略算法
2、常用的策略
策略名 | 实现类 | 说明 |
---|---|---|
轮询策略(默认) | RoundRobinRule | 轮询策略表示每次都顺序调用provider |
权重轮询策略(常用) | WeightedResponseTimeRule | 根据每个 provider 的响应时间分配一个权重,响应时间越长,权重越小,被选中的可能性越低。比如轮询策略,开启一个计时器,每 30 秒收集一次每个 provider 的平均响应时间,当信息足够时,给每个 provider附上一个权重,并按权重随机选择provider,高权越重的 provider会被高概率选中。 |
随机策略(不建议使用) | RandomRule | 随机选择一个provider |
最少并发数策略 | BestAvailableRule | 选择并发数最小的 provider |
在选定的负载均衡中重试 | RetryRule | 1.选定的负载均衡策略这个策略是轮询策略RoundRobinRule 2.该重试策略先设定一个阈值时间段,如果在这个阈值时间段内当选择 provider 不成功,则一直尝试采用“选定的负载均衡策略:轮询策略”最后选择一个可用的provider |
可用性敏感策略(同区域内服务集群中使用) | AvailabilityFilteringRule | 过滤性能差的 provider 1. 过滤掉在 注册中心中处于一直连接失败 provider 2. 过滤掉高并发的 provider |
区域敏感性策略(物理隔离分布式环境中使用) | ZoneAvoidanceRule | 1.以一个区域为单位考察可用性,对于不可用的区域整个丢弃,从剩下区域中选可用的provider 2.如果当前 ip 区域内有一个或多个实例不可用或响应变慢,都会降低该 ip 区域内其他 ip 被选中的权重。 |
三、修改全局默认策略
1、说明
在实际开发中,我们可能会遇到默认的负载均衡策略无法满足需求,从而需要更换其他的负载均衡策略。当然也可以根据具体业务需求自己写一个负载均衡算法类。关于Ribbon负载均衡的配置方式主要有两种:
-
代码中配置
-
配置文件中配置
2、代码中配置
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n75" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; color: rgb(184, 191, 198); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@Configuration
public class RibbonConfiguration {
@Bean
public IRule bestAvailableRule() {
// 轮询权重
return new WeightedResponseTimeRule()
// 过滤性能最差的
return new BestAvailableRule();
}
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}</pre>
3、在配置文件中配置
创建配置类的方式虽然能够实现个性化定义,但大量的配置类的维护还是比较麻烦的,所以在开发中个人建议使用配置类配置。这些配置项的前缀是客户端名称.ribbon.NFLoadBalancerRuleClassName=策略对应的类名
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="yaml" cid="n78" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; color: rgb(184, 191, 198); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">test-ribbon-server-02:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule</pre>
4、其它配置项
NFLoadBalancerRuleClassName
比较重要,我们可以通过这个配置项定制需要的负载均衡规则,可以是ribbon提供的原生的几种规则类,也可以是自己实现的规则类,这些类都实现了IRule接口。
-
NFLoadBalancerRuleClassName
指定负载均衡器的实现类。 -
NFLoadBalancerPingClassName
用于配置查看服务器是否存活。 -
NIWSServerListClassName
是服务器列表的处理类,用来维护服务器列表的。Ribbon已经实现了动态服务器列表。 -
NIWSServerListFilterClassName
是服务器的拦截类。
5、测试代码
客服端
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="" cid="n92" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; color: rgb(184, 191, 198); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@SpringBootApplication
public class RibbonApplication {
public static void main(String[] args) {
SpringApplication.run(RibbonApplication.class, args);
}
}</pre>
控制层
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="java" cid="n94" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; color: rgb(184, 191, 198); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;">@RestController
@Slf4j
public class RibbonController {
@Resource
RestTemplate restTemplate;
@RequestMapping("/service1")
public String testService1() {
String port = restTemplate.getForObject("http://test-ribbon-server-01/", String.class);
log.info("响应的端口是: {}", port);
return port;
}
@RequestMapping("/service2")
public String testService2() {
String port = restTemplate.getForObject("http://test-ribbon-server-02/", String.class);
log.info("响应的端口是: {}", port);
return port;
}
}</pre>
测试效果图
image image四、总结
-
尽量使用配置文件配置,配置文件满足不了需求的情况下再考虑使用代码配置
-
在同一个微服务内尽量保持单一性,例如统一使用配置文件配置,尽量不要两种方式混用,以免增加错误定位问题的复杂度
-
优先级: 配置文件 > java代码 > 默认
五、其它
1、loadbalancer(了解)
Spring Cloud Hoxton中使用loadbalancer实现来替代Ribbon的版本,但目前还不是很成熟
注意排除ribbon依赖,不然loadbalancer 无效
<pre spellcheck="false" class="md-fences md-end-block ty-contain-cm modeLoaded" lang="xml" cid="n110" mdtype="fences" style="box-sizing: border-box; overflow: visible; font-family: Monaco, Consolas, "Andale Mono", "DejaVu Sans Mono", monospace; margin-top: 0px; margin-bottom: 20px; font-size: 0.9rem; display: block; break-inside: avoid; text-align: left; white-space: normal; background: rgb(51, 51, 51); position: relative !important; padding: 10px 10px 10px 30px; width: inherit; color: rgb(184, 191, 198); font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; text-decoration-style: initial; text-decoration-color: initial;"><dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-loadbalancer</artifactId>
</dependency></pre>
2、Nacos负载均衡
其实Nacos Client已经有负载均衡的能力,Spring Cloud Alibaba为什么还要去整合Ribbon呢?主要是为了符合Spring Cloud标准。Spring Cloud Commons有个子项目 spring-cloud-loadbalancer
,该项目制定了标准,用来适配各种客户端负载均衡器(虽然目前实现只有Ribbon,但Hoxton就会有替代的实现了,但不成熟)。
Spring Cloud Alibaba遵循了这一标准,所以整合了Ribbon,而没有去使用Nacos Client提供的负载均衡能力。
网友评论