目录
一.细粒度配置负载均衡算法
1.Ribbon细粒度配置某个微服务使用指定负载均衡算法
2.Ribbo全局配置负载均衡算法
3.Ribbon配置
4.基于权重的负载均衡算法
二.扩展Ribbon-同集群优先调用
三.扩展Ribbon-基于元数据的版本控制
文章主要内容在文章(包括Ribbin介绍以及相关的其它内容):负载均衡:Ribbon (配置文件配置篇)
本文只记录如何用Java代码配置Ribbon,与配置文件配置对应。
以下步骤均在服务调用方操作,本文为:content-center,假设其需要调用user-center
a.创建configuration包
b.创建类UserCenterRibbonConfiguration.java (此类可作为user-center的专属配置类,也就是说,访问user-center时此配置生效)
注意注解@RibbonClient的类,在下面的步骤会创建。
package com.shixin.contentcenter.configuration;
@Configuration
@RibbonClient(name = "user-center",configuration = RibbonConfiguration.class)
public class UserCenterRibbonConfiguration {
}
c.创建ribbonconfiguration包(注意此包的位置:在启动类所在包之外)
为什么在启动类所在包之外:可以看到RibbonConfiguration类上有@Configuration注解,使用Ctrl+左键 进入注解可以发现,@Configuration上一层还有@Component注解。再观察启动类中@SpringBootApplication注解,其本质也是一个组合注解,其中包含注解@ComponentScan注解,这个注解是用来扫描Component的(包括@Component,@Controller,@Service......),自然也包括使用@Conponent作为组合注解的@Configuration注解,默认扫描的位置是当前启动类所在的包以及启动类所在包下面的所有可扫描注解。
这里Ribbon配置类的@Configuration注解不能被扫描到,由@SpringBootApplication说扫描的上下文称为主上下文,而Ribbon也会有一个上下文,是一个子上下文,若父子上下文所扫描的包重叠,会导致很多意想不到的问题。(如事务不生效,在本实例会导致无法细粒度配置(不仅调用user-center会使用此配置,掉用其他微服务也会使用此配置))
在以前用配置文件配置Spring,SpringMVC时,Spring的配置文件(application.xml)会扫描除Controller注解意外的其它注解(主上下文),而Spring MVC的配置类(Spring-mvc.xml)会只扫描Controller注解(子上下文),这也是为了避免父子上下文重叠的问题。
官方文档介绍:
大概意思就是Ribbon要使用@Configuration注解,但是又不能被主上下文扫描到。否则就会由所有程序共享@RibbonClient(在类UserCenterRibbonConfiguration中使用到),如果您使用@ComponentScan
(或@SpringBootApplication
),则需要采取措施避免将其包含在内(例如,您可以将其放在单独的,不重叠的程序包中,或在其中指定要扫描的程序包@ComponentScan
)
d.创建类RibbonConfiguration.java
package ribbonconfiguration;
@Configuration
public class RibbonConfiguration {
@Bean
public IRule ribbonRule(){
return new RandomRule();
}
/*
//Nacos基于权重的算法,可以在Nacos的控制台给微服务设置权重。不通过配置,在控制台直接设置,此种算法不生效,且暂未找到如何在配置文件中配置此种算法。
@Bean
public IRule ribbonRule(){
return new NacosWeightRule();
}
*/
}
在UserCenterRibbonConfiguration类加了@Configuration注解之后,此配置类会被扫描,通过以上四步,就可以对user-center(特定的微服务)指定特定的负载均衡算法。
a.父子上下文重叠(不推荐使用,此种做法虽能达到目的,但其实是一种异常行为)
b.修改UserCenterRibbonConfiguration类上的注解为 @RibbonClients
package com.shixin.contentcenter.configuration;
@Configuration
//@RibbonClient(name = "user-center",configuration = RibbonConfiguration.class)
@RibbonClients(defaultConfiguration = RibbonConfiguration.class)
public class UserCenterRibbonConfiguration {
}
可配置项:
配置解读:
其配置方法和负载均衡算法配置方法一样,如只需要在RibbonConfiguration中修改:
package ribbonconfiguration;
@Configuration
public class RibbonConfiguration {
@Bean
public IRule ribbonRule(){
return new RandomRule();
}
//如此配置Ribbon就会把Ping的规则改成PingUrl
@Bean
public IPing ping(){
return new PingUrl();
}
}
a.在configuration下创建类NacoWeightRule.java
package com.shixin.contentcenter.configuration;
/**
* @Author: shixin
* @Date: 2020/12/23 22:33
* @Version 1.0
* 实现基于权重的负载均衡算法
*/
@Slf4j
public class NacosWeightRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
//读取配置文件并初始化
}
@Override
public Server choose(Object o) {
try {
BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
//log.info("lb = {}",loadBalancer);
//想要请求的微服务的名称
String name = loadBalancer.getName();
//实现负载均衡算法
//拿到服务发现的相关API
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
//Nacos Client 自动通过基于权重的负载均衡算法,给我们选择一个实例
Instance instance = namingService.selectOneHealthyInstance(name);
log.info("选择是实例是port = {},instance = {}",instance.getPort(),instance);
return new NacosServer(instance);
} catch (NacosException e) {
return null;
}
}
}
b.如果是想局部配置,可参照文章:Ribbon介绍 (Java配置文件配置篇),第七章第一节,将类全路径更换即可。
c.如果是想全局配置,可参照本文第一章第一节和第二节。
另附文章:扩展Ribbon支持Nacos权重的三种方式
#和上面给予权重的负载均衡算法一样,也需要编写一个同集群优先调用的类
#创建类NacosSameClusterWeightedRule.java
package com.shixin.contentcenter.configuration;
/**
* @Author: shixin
* @Date: 2020/12/23 22:55
* @Version 1.0
*/
@Slf4j
public class NacosSameClusterWeightedRule extends AbstractLoadBalancerRule {
@Autowired
private NacosDiscoveryProperties nacosDiscoveryProperties;
@Override
public void initWithNiwsConfig(IClientConfig iClientConfig) {
}
@Override
public Server choose(Object o) {
try {
//可以拿到配置文件中的集群名称
String clusterName = nacosDiscoveryProperties.getClusterName();
BaseLoadBalancer loadBalancer = (BaseLoadBalancer) this.getLoadBalancer();
//想要请求的微服务的名称
String name = loadBalancer.getName();
//拿到服务发现的相关API
NamingService namingService = nacosDiscoveryProperties.namingServiceInstance();
//找到指定服务的所有实例 A
List instances = namingService.selectInstances(name, true);
//获取指定实例的元数据
//instances.get(0).getMetadata();
//过滤出相同集群下的所有实例 B
List sameClustInstances = instances.stream()
.filter(instance -> {
return Objects.equals(instance.getClusterName(), clusterName);
})
.collect(Collectors.toList());
//如果B是空 就用A
List instancesToBeChose = new ArrayList<>();
if (CollectionUtils.isEmpty(sameClustInstances)){
instancesToBeChose = instances;
log.warn("发生跨集群调用,name = {}, clusterName = {}",name,clusterName);
}else {
instancesToBeChose = sameClustInstances;
log.warn("发生同集群调用,name = {}, clusterName = {}",name,clusterName);
}
//基于权重的负载均衡算法,返回一个实例
Instance instance = ExtendBalancer.getHostByRandomWeight2(instancesToBeChose);
log.info("选择的实例是port = {},instance = {}",instance.getPort(),instance);
return new NacosServer(instance);
} catch (NacosException e) {
log.error("发生异常",e);
return null;
}
}
}
class ExtendBalancer extends Balancer{
public static Instance getHostByRandomWeight2(List hosts){
return getHostByRandomWeight(hosts);
}
}
#把RibbonConfiguration.java中加载的Bean改成NacosSameClusterWeightedRule类
#检查UserCenterRibbonConfiguration类中注解是否为RibbonConfiguration即可
#此种配置需要Java代码配合配置文件使用,配置文件中添加
cloud:
nacos:
discovery:
#集群名,可以按照需求更改
cluster-name: SZ
链接:扩展Ribbon支持基于元数据的版本管理