目录
Ribbon 是什么?
Ribbon 是Netflix 提供的开源客户端负载均衡器,他提供了多种负载均衡规则;
如何使用Ribbon?
1. 引入依赖
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
2. 加注解(RestTemplate整合Ribbon)
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
3. 编码
将RestTemplate 调用接口中的ip端口,替换成注册在nacos服务上的服务名称
eg:
http://alibaba-nacos/config/get -- alibaba-nacos 是被调用方注册在nacos 上的服务名
Ribbon 是由哪些组件构成?
接口 | 作用 | 默认值 |
---|---|---|
IclientConfig | 读取配置 | DefaultClientConfigImpl |
IRule | 负载均衡规则,选择实例 | ZoneAvoidanceRule |
IPing | 筛选掉ping不通的实例 | DumyPing (该类什么不干,认为每个实例都可用,都能ping通) |
ServerList | 交给Ribbon的实例列表 | Ribbon:ConfigurationBasedServerList Spring Cloud Alibaba: NacosServerList |
ServerListFilter | 过滤掉不符合条件的实例 | ZonePreferenceServerListFilter |
ILoadBalancer | Ribbon的入口 | ZoneAwareLoadBalancer |
ServerListUpdater | 更新交给Ribbon的List的策略 | PollingServerListUpdater |
Ribbon 内置的负载规则有哪些?
规则名称 | 特点 |
---|---|
AvailabilityFilteringRule | 过滤掉一直连接失败的被标记为circuit tripped(电路跳闸)的后端Service,并过滤掉那些高并发的后端Server或者使用一个AvailabilityPredicate来包含过滤Server的逻辑,其实就是检查status的记录的各个Server的运行状态 |
BestAvailableRule | 选择一个最小的并发请求的Server,逐个考察Server,如果Server被tripped了,则跳过 |
RandomRule | 随机选择一个Server |
RetryRule | 对选定的负责均衡策略机上充值机制,在一个配置时间段内当选择Server不成功,则一直尝试使用subRule的方式选择一个可用的Server |
RoundRobinRule | 轮询选择,轮询index,选择index对应位置Server |
WeightedResponseTimeRule | 根据相应时间加权,相应时间越长,权重越小,被选中的可能性越低 |
ZoneAvoidanceRule | (默认是这个)负责判断Server所Zone的性能和Server的可用性选择Server,在没有Zone的环境下,类似于轮询(RoundRobinRule ) |
Ribbon 如何细力度配置负载规则?
Ribbon 规则配置支持代码配置和配置属性方式配置,两种方式各有利弊。详情如下
代码配置
/**
* name 被调用接口nacos服务名称
* configuration ribbion 负责规则实现类
*/
@Configuration
@RibbonClient(name = "alibaba-nacos",configuration = RibbonRuleConfig.class)
public class RibbonConfig {
}
@Configuration
public class RibbonRuleConfig {
@Bean
public IRule getRandomRule(){
return new RoundRobinRule();
}
}
注意: RibbonRuleConfig 放置的位置需要是spring boot 启动类外的包内,否则会导致父子上下文重叠,从而引发问题;如果Ribbon 配置过程中发生了父子上下文重叠,会产生什么问题呢?
答案是如果RibbonRuleConfig配置在Spring boot 启动类同一个包内,那么这个Ribbon 负载规则 不在是针对服务的,而是一个全局的配置项了。到时候访问其他服务的接口也会调用RibbonRuleConfig 定义的负载规则,无法达到自定义细粒度Ribbon 负载规则配置了
关于父子上下文重叠其他问题可以参考https://blog.csdn.net/hhcccchh/article/details/104022770
配置属性方式
alibaba-nacos:
ribbon:
NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RoundRobinRule
其中 alibaba-nacos 是待调用服务nacos 服务名
NFLoadBalancerRuleClassName 指定的是负责规则类
两种方式对比
代码配置:
优点: 基于代码配置,更加灵活
缺点: 编写时需要注意父子上下文重叠问题,并且每次更改发布需要重新打包部署
配置文件:
优点: 配置直观,修改无需重新部署
缺点: 极端条件下,没有代码配置灵活。但是99% 的情况其实是可以通过配置属性方式来实现的
Ribbon 如何开启全局配置?
/**
*和 细力度配置相对比来说,Ribbon 的全局配置就是将@RibbonClient 替换成@RibbonClients ,细粒度皮*配置的Configuration 变成defaultConfiguration
*
*/
@RibbonClients(defaultConfiguration = RibbonConfig.class)
public class RibbonConfig {
}
Ribbon 如何开启饥饿加载?
Ribbion 默认是懒加载的方式,及服务第一调用的时候,才去加载。会导致第一次访问服务的时候速度缓慢.开启饥饿加载只需要在配置文件配置
开启饥饿加载配置方式:
ribbon:
eager-load:
enabled: true
clients: alibaba-nacos
alibaba-nacos 是注册在nacos上的服务名称,多个之间用逗号分隔
Ribbon 如何基于Nacos 的权重实现负载规则?
Nacos 权重配置的优点是可以基于服务器性能进行对服务器资源的在分配。且可以辅助我们达到服务器灰度下线的能力;实现如下
package com.springcloud.alibaba.consumer.nacosconfig;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.discovery.NacosDiscoveryClient;
import com.alibaba.cloud.nacos.ribbon.ExtendBalancer;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.exception.NacosException;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import java.util.List;
/**
* 根据nacos client 进行设置nacos权重匹配规则
*/
@Slf4j
public class NacosWeightRandomV2Rule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Autowired
private NacosServiceManager nacosServiceManager;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object o) {
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
// 获取服务的名称
String name = loadBalancer.getName();
log.info("-----name:{}------",name);
try {
NamingService namingService = nacosServiceManager.getNamingService(nacosDiscoveryProperties.getNacosProperties());
Instance instance = namingService.selectOneHealthyInstance(name);
return new NacosServer(instance);
} catch (NacosException e) {
e.printStackTrace();
return null;
}
}
}
配置文件添加对自定义负载规则的引用:
alibaba-nacos:
ribbon:
NFLoadBalancerRuleClassName: com.springcloud.alibaba.consumer.nacosconfig.NacosWeightRandomV2Rule
Ribbon 如何有些调用同一机房的集群服务?
更多情况下,为了容灾,我们会对集群进行异地部署。在使用的时候,为了性能,我们常常需要调用同一机房的服务
package com.springcloud.alibaba.consumer.nacosconfig;
import com.alibaba.cloud.nacos.NacosDiscoveryProperties;
import com.alibaba.cloud.nacos.NacosServiceManager;
import com.alibaba.cloud.nacos.ribbon.ExtendBalancer;
import com.alibaba.cloud.nacos.ribbon.NacosServer;
import com.alibaba.nacos.api.naming.NamingService;
import com.alibaba.nacos.api.naming.pojo.Instance;
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.AbstractLoadBalancerRule;
import com.netflix.loadbalancer.DynamicServerListLoadBalancer;
import com.netflix.loadbalancer.ILoadBalancer;
import com.netflix.loadbalancer.Server;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* @Author zj
* @Date 2021/1/12 16:25
* ribbon 同机房集群优先调用,同机房宕掉后会调用其他机房集群服务
*/
@Slf4j
public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Autowired
private NacosServiceManager nacosServiceManager;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object o) {
try {
// 获取配置文件中的当前服务的集群名称
String clusterName = nacosDiscoveryProperties.getClusterName();
DynamicServerListLoadBalancer loadBalancer = (DynamicServerListLoadBalancer) getLoadBalancer();
// 获取服务名称
String name = loadBalancer.getName();
NamingService namingService = nacosServiceManager.getNamingService(nacosDiscoveryProperties.getNacosProperties());
// 获取健康实例的所有服务
List<Instance> instances = namingService.selectInstances(name, true);
if (CollectionUtils.isEmpty(instances)){
return null;
}
// 进行筛选获取相同集群下的服务实例
List<Instance> instancesToChoose = instances;
if (!StringUtils.isEmpty(clusterName)){
List<Instance> collect = instances.stream().filter(instance -> Objects.equals(clusterName, instance.getClusterName())).collect(Collectors.toList());
if (!CollectionUtils.isEmpty(collect)){
instancesToChoose = collect;
}else{
log.info("跨机房调用");
}
}
Instance hostByRandomWeight2 = ExtendBalancer.getHostByRandomWeight2(instancesToChoose);
return new NacosServer(hostByRandomWeight2);
}catch (Exception e){
e.printStackTrace();
log.error(e.getMessage());
}
return null;
}
}
配置文件添加对自定义规则的引用
alibaba-nacos:
ribbon:
NFLoadBalancerRuleClassName: com.springcloud.alibaba.consumer.nacosconfig.NacosSameClusterWeightedRule
Ribbon 如何基于元数据的方式实现不同版本调用不同服务的能力?
参考: http://www.imooc.com/article/288674
总结:
通过Ribbon 的基础学习,对于负载规则,我们又多了一个思路去实现。本章博文是基于对大目老师Spring cloud alibaba Ribbon 课程的一个记录和总结。