基于Ribbon自定义负载均衡策略

2023-11-07

微服务间通过FeignClient互相调用默认使用的是ribbon的轮询负载均衡策略,而实际场景中我们可能需要自定义一些规则或者约束来实现特定的负载均衡策略。

背景:微服务开发,多租户,API接口隔离,这些在现在开发过程中会经常遇到的问题。服务多节点部署,同时每个service实例都有特定的标签或属性。比如instanceA是提供给租户tenantA服务的,instanceB是提供给租户tenantB服务的,instance是提供通用的服务;再比如instanceA只提供数据处理能力A,携带标签g_data_A,那么一个B请求就不应该被服务注册中心提供instanceA来处理,也就是做了接口层的隔离。为了实现以上特定需求,我们不能使用默认的ribbon负载均衡策略,需要自定义一个规则。
在这里插入图片描述
Spring Cloud的ribbon组件为我们提供了诸多负载均衡规则,这些规则都是通过一个抽象接口IRule来扩展的,IRule接口定义了选择负载均衡策略的基本操作。通过调用choose()方法,就可以选择具体的负载均衡策略。

public interface IRule{
    /*
     * choose one alive server from lb.allServers or
     * lb.upServers according to key
     * 
     * @return choosen Server object. NULL is returned if none
     *  server is available 
     */

    public Server choose(Object key);
    
    public void setLoadBalancer(ILoadBalancer lb);
    
    public ILoadBalancer getLoadBalancer();    
}

抽象类AbstractLoadBalancerRule实现了IRule接口和IClientConfigAware接口,主要对IRule接口的2个方法进行了简单封装。我们通过集成抽象类AbstractLoadBalancerRule即可自定义负载均衡规则。

public abstract class AbstractLoadBalancerRule implements IRule, IClientConfigAware {

    private ILoadBalancer lb;
        
    @Override
    public void setLoadBalancer(ILoadBalancer lb){
        this.lb = lb;
    }
    
    @Override
    public ILoadBalancer getLoadBalancer(){
        return lb;
    }      
}

ribbon已实现多种负载均衡规则,如下:

1,轮询策略(RoundRobinRule)

2,随机策略(RandomRule)

3,可用过滤策略(AvailabilityFilteringRule)

策略描述:过滤掉连接失败的服务节点,并且过滤掉高并发的服务节点,然后从健康的服务节点中,使用轮询策略选出一个节点返回。

4,响应时间权重策略(WeightedResponseTimeRule)

策略描述:根据响应时间,分配一个权重weight,响应时间越长,weight越小,被选中的可能性越低。

5,轮询失败重试策略(RetryRule)

6,并发量最小可用策略(BestAvailableRule)

7,ZoneAvoidanceRule

策略描述:复合判断server所在区域的性能和server的可用性,来选择server返回。

根据我们的实际需求,我们需要根据特定的约束(租户和接口隔离)来实现自定义负载均衡规则,而ribbon提供了一个AbstractLoadBalancerRule的抽象子类PredicateBasedRule,他是通过AbstractServerPredicate来断言输入的server(从注册中心获取)是否符合需求,不符合则跳过,继续对下一个server进行断言,这个PredicateBasedRule就很符合我们的实际需求,下面我们就可以用他来自定义我们的规则。

public abstract class PredicateBasedRule extends ClientConfigEnabledRoundRobinRule {
    
    public abstract AbstractServerPredicate getPredicate();
        
    @Override
    public Server choose(Object key) {
        ILoadBalancer lb = getLoadBalancer();
        Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getAllServers(), key);
        if (server.isPresent()) {
            return server.get();
        } else {
            return null;
        }       
    }
}

我们定义一个动态负载均衡规则类DynamicBalanceRule实现PredicateBasedRule,同时重写其choose(Object key)方法,并实现其getPredicate()方法来自定义断言规则。

1、服务消费方通过FeignClient调用提供方的接口时,会进入到ribbon默认的负载均衡规则,我们自定义的DynamicBalanceRule如果要生效,需要在配置文件中配置(或者通过编码方式),同时我们希望这个规则只应用于服务提供方Whatever-Service的调用,而不影响其他的微服务调用负载均衡策略,那么在配置中我们就需要指定注册中心服务的名称,所以在消费者端的配置如下(指定DynamicBalanceRule的完整类路径即可):

# 类DynamicBalanceRule的完整类路径
Whatever-Service.ribbon.NFLoadBalancerRuleClassName=com.*.*.*.DynamicBalanceRule

2、服务提供方Whatever-Service在启动的时候是携带了所属租户和提供特定接口能力的标签(tags)的,而我们消费方发出请求的时候肯定是知道自己当前请求的用户所属的租户,也知道当前调用的接口信息 。那么只要和提供方的tag匹配即可认为这个实例是可用的,也即断言成立。

具体实现如下:

1)首先我们在通过FeignClient调用提供者提供的接口前,将当前请求所携带的租户和接口信息封装到一个对象中IsolationAttribute,即创建new IsolationAttribute(tenantDomain, interfaceGroup);

2)然后通过ThreadLocal将IsolationAttribute放入当前http线程上下文中TenantIsolationThreadLocal.set(new IsolationAttribute(tenantDomain, interfaceGroup));

3)发起对提供方接口的调用whateverServiceFeignClient.doSomethingBad();

4)此时业务执行会进入到DynamicBalanceRule的choose(Object key)方法。

@Override
	public Server choose(Object key) {
		try {
			// 从指定的租户域获取server
			ILoadBalancer lb = getLoadBalancer();
			Optional<Server> server = getPredicate().chooseRoundRobinAfterFiltering(lb.getReachableServers(), key);
			if (server.isPresent()) {
				return server.get();
			}
			return null;
		} finally {
			TenantIsolationThreadLocal.remove();
		}
	}

3、执行getPredicate()方法,我们需要实现自定义的getPredicate()来获取一个自定义的断言。我们定义一个断言DynamicPredicate如下:

public class DynamicPredicate extends AbstractServerPredicate {

	public static String defaultTenantDomainKey = DefaultTenantConfig.defaultTenantDomainKey;

	public static String defaultInterfaceGroupKey = DefaultTenantConfig.defaultInterfaceGroupKey;
	
	private String tenant;
	
	private String group;

	public DynamicPredicate() {

	}

	public DynamicPredicate(String tenant, String group) {
		this.tenant = tenant;
		this.group = group;
	}

	@Override
	public boolean apply(PredicateKey input) {
		return !shouldSkipServer(input.getServer());
	}

	private boolean shouldSkipServer(Server server) {
		if (server instanceof ConsulServer) {
			ConsulServer consulServer = (ConsulServer) server;
			Map<String, String> metadata = consulServer.getMetadata();
			if (metadata.containsKey(defaultTenantDomainKey)) {
				String serverTenantDomain = metadata.get(defaultTenantDomainKey);
				if (!tenant.equalsIgnoreCase(serverTenantDomain)) {
					return true;
				}
			} else {
				return true;
			}
			if (StringUtils.isEmpty(group)) {
				return false;
			}
			String serverGroup = server.getMetaInfo().getServerGroup();
			if (StringUtils.isEmpty(serverGroup)) {
				serverGroup = metadata.get(defaultInterfaceGroupKey);
			}
			if (StringUtils.isEmpty(serverGroup) || !serverGroup.contains(group)) {
				return true;
			}
		}
		return false;
	}

}

以上DynamicPredicate继承抽象类AbstractServerPredicate,并实现顶层接口的boolean apply(@Nullable T input)方法,在apply方法中断言当前输入的server是否符合我们的实际需求。在shouldSkipServer(Server server)方法中我们需要判断租户域是否匹配,接口是否匹配,完全匹配则断言成立。

4、根据自定义的断言,我们就可以使用Optional server = getPredicate().chooseRoundRobinAfterFiltering(lb.getReachableServers(), key)来通过轮询策略来筛选符合输入条件的的server。同时,如果我们通过自定义的断言和规则没有找到合适的server,那么我们可以通过FallbackPredicate来实现当找不到server时的处理策略,比如没有找到合适的server时认定所有输入的server对我们自定义的断言都成立,配置如下:

CompositePredicate compositePredicate = CompositePredicate
		.withPredicate(new DynamicPredicate(currentTenantDomain, currentInterfaceGroup))
	        .addFallbackPredicate(AbstractServerPredicate.alwaysTrue()).build();

由此,我们自定义的负载均衡策略已完成。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于Ribbon自定义负载均衡策略 的相关文章

随机推荐

  • ARP攻击与防范实践

    1 ARP中间人攻击原理 攻击原理 攻击者利用ARP机制 让PC A学习到IP S与MAC B的映射关系 又让Server学习到IP A 与MAC B的映射关系 如此一来 PC A与Server之间交互的IP报文都会经过攻击者中转 漏洞分析
  • 机器学习中的 AUC ROC 曲线指南:什么是特异性-附源码

    介绍 您已经构建了机器学习模型 那么下一步是什么 您需要评估并验证它有多好 或多坏 以便您可以决定是否实施它 这就是 AUC ROC 曲线的用武之地 这个名字可能拗口 但它只是说我们正在计算 接收者操作特征 ROC 的 曲线下面积 AUC
  • 在IDEA中Spring boot配置热部署无效问题解决方式

    只要在pom文件中添加下面代码段即可
  • html 图片加载优化

    在项目开发中 如果页面中图片过多 就会出现页面加载缓慢的问题 优化的方案为图片降级 div class box img alt img alt div 首先考虑使用webp格式的图片 因为webp格式的图片拥有无损压缩和有损压缩两种模式而且
  • 面试题篇-09-RocketMQ相关面试题

    文章目录 1 RocketMQ如何保证消息有序消费 2 RocketMQ和Kafka 快的飞起 底层存储有什么不同 2 1 什么是 零拷贝 2 1 1 传统IO数据读写 2 1 2 mmap优化 3次切换 3次拷贝 2 1 3 SendFi
  • 回溯法——两类问题的递归方法解析

    最近在学习回溯法 有些心得 记录下来 之前学习了分治法 动态规划 和回溯法拿在一起考虑 发现其利用递归的思想很巧妙 我自己总结的认为递归的核心思想就是考虑整体中所有个体都有的一般规律 将其描述出来 然后进行递归 到下一个个体 当到达分解的尾
  • 点集配准—CPD(Coherent Point Drift) (附python代码)

    点集配准 CPD Coherent Point Drift 算法是一种点云配准算法 用于将两个点云对齐 该算法最初由Myronenko 和 Song 在2009年提出 CPD算法的核心思想是将一个点集看作是由一个概率密度函数生成的样本 然后
  • 吴恩达机器学习(五) 代价函数(2)

    简化的线性假设函数 我们的目的是优化算法 得到使J 1 最小的 1的值 未简化的线性假设函数 登高图的椭圆可以理解为3D图像的俯视图 最小的椭圆是碗底 在同一条椭圆上的点具有相同的J 0 1 的值
  • 区块链中的密码学应用

    区块链中应用到的密码学主要包括以下几方面 数字摘要 区块链本质上是一种分布式数据存储方式 每一个数据区块之间靠数字摘要建立起联系 比如比特币中每一个区块都包含了它前一个区块的摘要值 因此数字摘要是区块链中应用最广泛的密码学技术 也是区块链的
  • SQL删除语句

    1 delete 可指定删除一行或多行 不改变表结构 语句 delete from table where 2 drop 完全删除 包括表结构 语句 drop table 表名称 drop database 数据库名 drop index
  • matlab给定状态方程用欧拉法_基于SPH法的水滴入钢板仿真

    光滑粒子流体动力学 SPH Smoothedparticle hydrodynamics 方法是近30年发展起来的一种新的纯拉格朗日无网格粒子法 它最初是用于解决三维开放空间的天体物理学问题 现已被广泛地研究和扩展 并被应用于具有材料强度的
  • 注解@Qualifier的使用

    近期在整理一些spring的知识点 遇到关于 Qualifier这个注解的使用 记录一下 先说这个注解的作用 和autowired对比 Autowired默认是根据类型进行注入的 因此如果有多个类型一样的Bean候选者 则需要限定其中一个候
  • LeetCode第283题解法与思考

    第283题的要求如下 给定一个数组 nums 编写一个函数将所有 0 移动到数组的末尾 同时保持非零元素的相对顺序 示例 输入 0 1 0 3 12 输出 1 3 12 0 0 说明 必须在原数组上操作 不能拷贝额外的数组 尽量减少操作次数
  • python——时间绑定

    一个Tkinter主要跑在mainloop进程里 Events可能来自多个地方 比如按键 鼠标 或是系统事件 Tkinter提供了丰富的方法来处理这些事件 对于每一个控件Widget 你都可以为其绑定方法function widget bi
  • stm32——定时器之中断

    TIM 定时器 是STM32中比较重要的外设 定时器分为三种 分为为高级定时器 通用定时器 和基本定时器 本节就以通用定时器讲解 定时器中断 就是指每隔一段确定的时间 cpu就自动进入中断 执行响应的事件 那它是怎样实现这个功能的呢 首先它
  • 数据结构(C语言版 第2版)课后习题答案 严蔚敏 编著

    转自 https blog csdn net Bamboo shui article details 72433523 原文没第八章答案 数据结构 C语言版 第2版 课后习题答案 严蔚敏 等 编著 仅供参考 还是自己认真做了再看 第1章 绪
  • 油猴(Tampermonkey)安装教程

    油猴简介 Tampermonkey 油猴 是一款免费的浏览器扩展和最为流行的用户脚本管理器 它适用于 Chrome Microsoft Edge Safari Opera Next 和 Firefox 虽然有些受支持的浏览器拥有原生的用户脚
  • Informer讲解PPT介绍【超详细】--AAAI 2021最佳论文:比Transformer更有效的长时间序列预测

    文章目录 Abstract 一 informer重温讲解PPT简洁 超详细 1 1 title 1 2 Background 1 3 LSTF 问题的提出 1 4 Transformer in LSTF problem 1 5 问题阐述 1
  • 常见的内存数据库有哪些

    Redis 键值存储数据库 常用于缓存 消息代理和应用程序数据处理 Memcached 分布式内存对象缓存系统 用于缓存Web应用程序数据 VoltDB 高速内存数据库 用于实时数据处理和实时决策 Aerospike 高性能的键值存储和文档
  • 基于Ribbon自定义负载均衡策略

    微服务间通过FeignClient互相调用默认使用的是ribbon的轮询负载均衡策略 而实际场景中我们可能需要自定义一些规则或者约束来实现特定的负载均衡策略 背景 微服务开发 多租户 API接口隔离 这些在现在开发过程中会经常遇到的问题 服