一、前言
本文将自定义负载均衡策略来实现权限策略
和同一集群优先使用带版本策略
,主要解决在本地开发环境启动相同服务时,调用服务会跑到其他人那里。
简要逻辑:在通过nacos注册服务时,添加version
参数绑定本地服务ip信息,在服务调用时,去获取nacos上注册服务信息,通过version
参数条件去指定调用具体服务。
ex: 服务A调用服务B
如果服务B的version参数值和服务A的version参数值一致,即会优先调用与服务A相同version值的服务B
如果没有与服务A相同version值的服务B,再去随机按权重调用服务B
环境:
- spring-boot-dependencies 2.3.2.RELEASE
- spring-cloud-dependencies Hoxton.SR8
- spring-cloud-alibaba-dependencies 2.2.3.RELEASE
二、自定义负载均衡策略
1、自定义负载均衡策略-权重
@Slf4j
public class BalancerWeightRule extends AbstractLoadBalancerRule {
@Autowired
private NacosServiceManager nacosServiceManager;
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
// 读取配置文件, 并且初始化, ribbon内部基本上用不上
}
/**
* 这个方法是实现负载均衡策略的方法
*/
@Override
public Server choose(Object key) {
try {
// 1、获取当前服务的分组名称
String groupName = this.nacosDiscoveryProperties.getGroup();
// 2、获取当前服务的负载均衡器
BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
// 3、获取目标服务的服务名
String serviceName = baseLoadBalancer.getName();
// 4、获取nacos提供的服务注册api
NamingService namingService = this.nacosServiceManager.getNamingService(this.nacosDiscoveryProperties.getNacosProperties());
// 5、根据目标服务名称和分组名称去获取服务实例,nacos实现了权重的负载均衡算法 false: 及时获取nacos注册服务信息
Instance toBeChooseInstance = namingService.selectOneHealthyInstance(serviceName, groupName, false);
BalancerInstanceUtil.printInstance(BalancerRuleTypeEnum.WEIGHT, toBeChooseInstance);
return new NacosServer(toBeChooseInstance);
} catch (NacosException e) {
log.error("自定义负载均衡策略-权重 调用异常: ", e);
return null;
}
}
}
2、自定义负载均衡策略-同一集群优先使用带版本实例
@Slf4j
public class BalancerVersionRule extends AbstractLoadBalancerRule {
@Autowired
private NacosServiceManager nacosServiceManager;
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object o) {
try {
// 1、获取当前服务的分组名称、集群名称、版本号
String groupName = this.nacosDiscoveryProperties.getGroup();
String clusterName = this.nacosDiscoveryProperties.getClusterName();
String version = this.nacosDiscoveryProperties.getMetadata().get("version");
// 2、获取当前服务的负载均衡器
BaseLoadBalancer baseLoadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
// 3、获取目标服务的服务名
String serviceName = baseLoadBalancer.getName();
// 4、获取nacos提供的服务注册api
NamingService namingService = this.nacosServiceManager.getNamingService(this.nacosDiscoveryProperties.getNacosProperties());
// 5、获取所有服务名为serviceName的服务实例 false: 及时获取nacos注册服务信息
List<Instance> allInstanceList = namingService.getAllInstances(serviceName, groupName, false);
// 6、过滤有相同集群的服务实例
List<Instance> sameClusterInstanceList = Lists.newLinkedList();
for (Instance instance : allInstanceList) {
if (instance.getClusterName().equals(clusterName)) {
sameClusterInstanceList.add(instance);
}
}
// 7、过滤相同版本的服务实例
List<Instance> sameVersionInstanceList = Lists.newLinkedList();
for (Instance instance : sameClusterInstanceList) {
if (version.equals(instance.getMetadata().get("version"))) {
sameVersionInstanceList.add(instance);
}
}
// 8、选择合适的服务实例
Instance toBeChooseInstance;
if (CollectionUtils.isEmpty(sameVersionInstanceList)) {
toBeChooseInstance = WeightedBalancer.chooseInstanceByRandomWeight(sameClusterInstanceList);
BalancerInstanceUtil.printInstance(BalancerRuleTypeEnum.VERSION_WEIGHT, toBeChooseInstance);
} else {
toBeChooseInstance = WeightedBalancer.chooseInstanceByRandomWeight(sameVersionInstanceList);
BalancerInstanceUtil.printInstance(BalancerRuleTypeEnum.VERSION, toBeChooseInstance);
}
return new NacosServer(toBeChooseInstance);
} catch (NacosException e) {
log.error("Nacos同一集群带版本优先调用异常: ", e);
return null;
}
}
}
版本策略所需随机权重类
public class WeightedBalancer extends Balancer {
public static Instance chooseInstanceByRandomWeight(List<Instance> instanceList) {
// 这是父类Balancer自带的根据随机权重获取服务的方法.
return getHostByRandomWeight(instanceList);
}
}
3、启用自定义负载均衡策略
@Configuration
@RibbonClients(defaultConfiguration = GlobalRibbonConfig.class)
public class CustomRibbonConfig {
}
@Slf4j
@Data
@Configuration
public class GlobalRibbonConfig {
@Value("${ribbon.rule-type:}")
private String ruleType;
@Bean
public IRule getRule() {
// 自定义负载均衡策略
BalancerRuleTypeEnum balancerRuleTypeEnum = BalancerRuleTypeEnum.getEnum(this.ruleType);
log.info("使用自定义负载均衡策略:[{}]", balancerRuleTypeEnum.getDesc());
switch (balancerRuleTypeEnum) {
case WEIGHT:
// 权重
return new BalancerWeightRule();
case VERSION:
// 同一集群优先带版本实例
return new BalancerVersionRule();
default:
// 默认权重
return new BalancerWeightRule();
}
}
}
通过配置ribbon.rule-type
参数值来选择使用哪一种负载均衡策略
ribbon:
rule-type: version
4、负载均衡策略枚举类和工具类
@Slf4j
@Getter
@AllArgsConstructor
public enum BalancerRuleTypeEnum {
/**
* 权重
*/
WEIGHT("weight", "权重"),
/**
* 同一集群使用相同版本
*/
VERSION("version", "同一集群使用相同版本"),
/**
* 同一集群无相同版本,使用权重
*/
VERSION_WEIGHT("version_weight", "同一集群无相同版本,使用权重");
/**
* 规则类型
*/
private final String type;
/**
* 规则描述
*/
private final String desc;
private static final List<BalancerRuleTypeEnum> LIST = Lists.newArrayList();
static {
LIST.addAll(Arrays.asList(BalancerRuleTypeEnum.values()));
}
/**
* 根据指定的规则类型查找相应枚举类
*
* @param type 规则类型
* @return 规则类型枚举信息
* @author zhengqingya
* @date 2020/9/13 18:46
*/
public static BalancerRuleTypeEnum getEnum(String type) {
for (BalancerRuleTypeEnum itemEnum : LIST) {
if (itemEnum.getType().equals(type)) {
return itemEnum;
}
}
log.warn("未找到指定的负载均衡策略,默认权重策略!");
return WEIGHT;
}
}
@Slf4j
public class BalancerInstanceUtil {
public static void printInstance(BalancerRuleTypeEnum ruleTypeEnum, Instance instance) {
if (instance == null) {
return;
}
log.info("自定义负载均衡策略-[{}] serviceName: [{}], clusterName: [{}], ip: [{}] port: [{}]",
ruleTypeEnum.getDesc(),
instance.getServiceName(),
instance.getClusterName(),
instance.getIp(),
instance.getPort()
);
}
}
三、配置ribbon饥饿加载模式
主要解决:在服务启动后,第1次访问可能会报错
产生原因:ribbon服务调用默认使用懒加载模式,即在调用的时候才会去创建相应的client
ribbon:
# 配置饥饿加载模式
eager-load:
# 开启饥饿加载模式
enabled: true
# 指定需要饥饿加载的服务名
clients:
- "demo"
- "system"
- "tool"
四、本文案例demo源码
https://gitee.com/zhengqingya/small-tools
今日分享语句:
我不敢休息,因为我没有存款。我不敢说累,因为我没有成就。我不敢偷懒,因为我还要生活。我能放弃选择,但是我不能选择放弃。坚强拼搏是我唯一的选择。