sentinel系统负载自适应流控

系统负载自适应流控

规则配置

规则创建

public class SystemRule extends AbstractRule {private double highestSystemLoad = -1;private double highestCpuUsage = -1;private double qps = -1;private long avgRt = -1;private long maxThread = -1;
}

SystemRule类包含了以下几个指标。

  • highestSystemLoad:对应 Dashboard 上的 LOAD 菜单,代表系统最高负载值,默认为 -1,只有大于等于 0.0 才生效。
  • avgRt:对应 Dashboard 上的 RT菜单,代表系统平均响应时间,默认为 -1,只有大于0才生效。
  • maxThread:对应 Dashboard 上的线程数菜单,代表系统允许的最大线程数,默认为 -1,只有大于 0 才生效。
  • qps:对应 Dashboard 上的入口 QPS 菜单,代表限流的阈值,默认为 -1,只有大于 0 才生效。
  • highestCpuUsage:对应 Dashboard 上的 CPU 使用率菜单,代表最高CPU 使用率,取值是 [0,1] 之间,默认为 -1,只有大于等于0.0才生效
监听器实例化和管理

这部分和之前的黑白名单差不多

系统负载自适应规则的核心类是 SystemRuleManager,它负责管理系统负载自适应规则的加载、更新和监听。当系统负载自适应规则发生变化时,SystemRuleManager 通过观察者模式通知相应的 RulePropertyListener 进行更新

创建监听器的代码位置

public final class SystemRuleManager {// 省略其它代码...private static AtomicBoolean checkSystemStatus = new AtomicBoolean(false);private static SystemStatusListener statusListener = null;private final static SystemPropertyListener listener = new SystemPropertyListener();private static SentinelProperty<List<SystemRule>> currentProperty = new DynamicSentinelProperty<List<SystemRule>>();// 创建单核线程池private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1,new NamedThreadFactory("sentinel-system-status-record-task", true));static {checkSystemStatus.set(false);// 初始化系统状态监听器statusListener = new SystemStatusListener();// 任务调度, 一秒执行一次statusListener的任务, 即监听系统的负载状态scheduler.scheduleAtFixedRate(statusListener, 0, 1, TimeUnit.SECONDS);// 初始化SystemRule监听器currentProperty.addListener(listener);}// 省略其它代码...
}

规则初始化

当调用SystemRuleManagerloadRules()

public static void loadRules(List<SystemRule> rules) {currentProperty.updateValue(rules);
}@Override
public boolean updateValue(T newValue) {if (isEqual(value, newValue)) {return false;}RecordLog.info("[DynamicSentinelProperty] Config will be updated to: {}", newValue);// 注意看这里, 和之前的黑白名单规则一样, 也是初始化了value = newValue;for (PropertyListener<T> listener : listeners) {// 遍历通知监听器listener.configUpdate(newValue);}return true;
}@Override
public synchronized void configUpdate(List<SystemRule> rules) {// 为了恢复这些系统设置到初始状态,以便重新进行监控和设置restoreSetting();// systemRules = rules;// 如果配置SystemRule, 那么遍历规则, 并加载规则if (rules != null && rules.size() >= 1) {for (SystemRule rule : rules) {// 加载系统配置,根据传入的SystemRule对象中的参数设置系统最高负载、CPU使用率、平均响应时间、最大线程数和QPSloadSystemConf(rule);}} else { // 如果没有配置SystemRule, 那么关闭系统自适应检查checkSystemStatus.set(false);}// 省略其它代码...
}

核心loadSystemConf()

此方法会判断是否配置了 LOAD、RT、THREAD、QPS、CPU,如果配置这些规则中的某一个,那么就将 checkSystemStatus 置为 true,也就是打开系统自适应功能

也就是说, 系统自适应功能是否开启就看这个方法

public static void loadSystemConf(SystemRule rule) {boolean checkStatus = false;// Check if it's valid.// highestSystemLoad参数大于等于0且小于当前最高系统负载,则更新最高系统负载,并标记为已设置if (rule.getHighestSystemLoad() >= 0) {highestSystemLoad = Math.min(highestSystemLoad, rule.getHighestSystemLoad());highestSystemLoadIsSet = true;checkStatus = true;}// 如果highestCpuUsage参数大于0且小于等于1,则更新CPU使用率的最高限制,并标记为已设置,如果大于1则记录警告日志if (rule.getHighestCpuUsage() >= 0) {if (rule.getHighestCpuUsage() > 1) {RecordLog.warn(String.format("[SystemRuleManager] Ignoring invalid SystemRule: "+ "highestCpuUsage %.3f > 1", rule.getHighestCpuUsage()));} else {highestCpuUsage = Math.min(highestCpuUsage, rule.getHighestCpuUsage());highestCpuUsageIsSet = true;checkStatus = true;}}// 如果果avgRt参数大于等于0,则更新平均响应时间的最高限制,并标记为已设置if (rule.getAvgRt() >= 0) {maxRt = Math.min(maxRt, rule.getAvgRt());maxRtIsSet = true;checkStatus = true;}// 如果maxThread参数大于等于0,则更新最大线程数的最高限制,并标记为已设置if (rule.getMaxThread() >= 0) {maxThread = Math.min(maxThread, rule.getMaxThread());maxThreadIsSet = true;checkStatus = true;}// 如果qps参数大于等于0,则更新QPS的最高限制,并标记为已设置if (rule.getQps() >= 0) {qps = Math.min(qps, rule.getQps());qpsIsSet = true;checkStatus = true;}// 根据上述值决定是否开启系统自适应检查checkSystemStatus.set(checkStatus);}

流程图如下
在这里插入图片描述

规则验证

SystemSlot是第六个执行的slot

public class SystemSlot extends AbstractLinkedProcessorSlot<DefaultNode> {@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable {// 检查系统规则SystemRuleManager.checkSystem(resourceWrapper, count);// 如果检查通过,继续执行后续的处理链fireEntry(context, resourceWrapper, node, count, prioritized, args);}@Overridepublic void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {fireExit(context, resourceWrapper, count, args);}
}

核心方法就是checkSystem()

public static void checkSystem(ResourceWrapper resourceWrapper, int count) throws BlockException {// 参数验证,资源为空直接放行if (resourceWrapper == null) {return;}// 判断系统自适应功能是否开启,如果没开启则直接放行。if (!checkSystemStatus.get()) {return;}// 判断资源的流量是否为入口流量,如果不是IN,则直接放行,也就是说Sentinel系统自适应限流只对入口流量生效,如果类型为OUT则直接放行if (resourceWrapper.getEntryType() != EntryType.IN) {return;}// 获取当前qps,如果当前qps大于SystemRule规则配置的阈值,则直接抛BlockException异常double currentQps = Constants.ENTRY_NODE.passQps();if (currentQps + count > qps) {throw new SystemBlockException(resourceWrapper.getName(), "qps");}// 获取当前线程,如果当前线程大于SystemRule规则配置的阈值,则直接抛BlockException 异常int currentThread = Constants.ENTRY_NODE.curThreadNum();if (currentThread > maxThread) {throw new SystemBlockException(resourceWrapper.getName(), "thread");}// 获取当前平均响应时间指标,如果当前平均响应时间大于SystemRule规则配置的阈值,则直接抛BlockException异常double rt = Constants.ENTRY_NODE.avgRt();if (rt > maxRt) {throw new SystemBlockException(resourceWrapper.getName(), "rt");}// 如果当前系统负载大于规则配置的系统负载,则采取bbr算法验证if (highestSystemLoadIsSet && getCurrentSystemAvgLoad() > highestSystemLoad) {// bbr算法if (!checkBbr(currentThread)) {throw new SystemBlockException(resourceWrapper.getName(), "load");}}// 判断当前CPU使用率是否大于SystemRule规则配置的阈值,如果大于,则抛出BlockException异常if (highestCpuUsageIsSet && getCurrentCpuUsage() > highestCpuUsage) {throw new SystemBlockException(resourceWrapper.getName(), "cpu");}
}// 使用BBR对负载进行校验
private static boolean checkBbr(int currentThread) {if (currentThread > 1 &&currentThread > Constants.ENTRY_NODE.maxSuccessQps() * Constants.ENTRY_NODE.minRt() / 1000) {return false;}return true;
}

上述有几个点需要说明

  1. BBR是什么?负载怎么获取的?
  2. Constants.ENTRY_NODE中的指标是什么存储进去的?
  3. CPU又是怎么获取的
BBR算法

BBR (Bottleneck Bandwidth and Round-trip propagation time) 是 Google 开发的一种拥塞控制算法,主要用于解决网络拥塞问题。下面我们将上面的代码进行拆解下:

  • 首先检查当前线程数是否大于 1,如果不是,则直接返回 true,表示通过 BBR 检查。
  • 如果当前线程数大于 1,那么检查当前线程数是否大于 (Constants.ENTRY_NODE.maxSuccessQps() * Constants.ENTRY_NODE.minRt() / 1000)
    • maxSuccessQps() 是每秒最大成功请求数
    • minRt() 是最小响应时间
    • 如果当前线程数大于这个计算值,那么返回 false,表示未通过 BBR 检查。否则,返回 true

用通俗的语言解释:检查当前线程数是否大于(每秒最大成功请求数 * 最小响应时间 / 1000),如果大于这个值,说明系统可能出现拥塞,返回 false,否则返回 true

举个例子,假设 currentThread 为 5,maxSuccessQps() 为 10,minRt() 为 200。那么计算值为 (10 * 200) / 1000 = 2。因为 currentThread 大于计算值,所以返回 false,表示未通过 BBR 检查。

checkBbr 方法的目的是在系统负载较高的情况下,通过限制并行线程数来防止系统过载

Constants.ENTRY_NODE相关说明

其实Constants.ENTRY_NODE的指标其实就是在ClusterNode中统计的, 这个ClusterNode专门用户统计某资源在全部Context内的指标

public final static ClusterNode ENTRY_NODE = new ClusterNode(TOTAL_IN_RESOURCE_NAME, ResourceTypeConstants.COMMON);

ClusterNode最终也是通过StatisticSlot统计QPS、Thread、avgRt 这三个指标, 可以看到下边类图的继承关系
在这里插入图片描述

观察一下StatisticSlot是怎么收集这个几个资源的, 下边展示核心代码, 非核心代码省略

public class StatisticSlot extends AbstractLinkedProcessorSlot<DefaultNode> {@Overridepublic void entry(Context context, ResourceWrapper resourceWrapper, DefaultNode node, int count, boolean prioritized, Object... args) throws Throwable {try {// 其它代码...if (resourceWrapper.getEntryType() == EntryType.IN) {// 通过线程数Constants.ENTRY_NODE.increaseThreadNum();// QPS通过数Constants.ENTRY_NODE.addPassRequest(count);}} catch (PriorityWaitException ex) {// 其它代码...if (resourceWrapper.getEntryType() == EntryType.IN) {// 拒绝线程数Constants.ENTRY_NODE.increaseThreadNum();}if (resourceWrapper.getEntryType() == EntryType.IN) {// 拒绝QPS数Constants.ENTRY_NODE.increaseBlockQps(count);}}}@Overridepublic void exit(Context context, ResourceWrapper resourceWrapper, int count, Object... args) {// // 获取当前时间作为响应时间long completeStatTime = TimeUtil.currentTimeMillis();// rt(此次请求所耗费 的时间)= 响应时间 - 开始时间long rt = completeStatTime - context.getCurEntry().getCreateTimestamp();// 如果是请求类型是 INif (resourceWrapper.getEntryType() == EntryType.IN) {// 则记录 rt 到 ClusterNoderecordCompleteFor(Constants.ENTRY_NODE, count, rt, error);}}
}

可以看到上边代码判断流量类型为 EntryType.IN, 才调用 Constants.ENTRY_NODE相关的方法统计QPS、Thread、avgRt

补充说明, 记录的开始时间并不是在StatisticSlot的入口方法entry(), 而是初始化资源的时

因为StatisticSlot已经是责任链的第三个 Slot 了,前面已经经过一些Slot和其他逻辑

public Entry(ResourceWrapper resourceWrapper, int count, Object[] args) {this.resourceWrapper = resourceWrapper;// 记录开始时间this.createTimestamp = TimeUtil.currentTimeMillis();this.count = count;this.args = args;
}
CPU相关指标
获取

Java提供了与之对应的API供我们获取CPU指标, sentinel直接在这个基础上进行了封装, 代码位于com.alibaba.csp.sentinel.slots.system.SystemStatusListener#run, 这个工具类可以改造为我们所用

public class SystemStatusListener implements Runnable {volatile double currentLoad = -1;volatile double currentCpuUsage = -1;volatile long processCpuTime = 0;volatile long processUpTime = 0;/*通过JMX获取操作系统的系统负载、CPU使用率等指标信息,并计算当前进程的CPU使用率。如果系统负载超过预设阈值,则记录系统状态日志*/@Overridepublic void run() {try {// 获取操作系统的MXBean实例OperatingSystemMXBean osBean = ManagementFactory.getPlatformMXBean(OperatingSystemMXBean.class);// 获取系统平均负载值currentLoad = osBean.getSystemLoadAverage();// 获取系统CPU使用率, 0.0代表所有CPU完全空闲,1.0代表所有CPU一直在满负荷运行double systemCpuUsage = osBean.getSystemCpuLoad();RuntimeMXBean runtimeBean = ManagementFactory.getPlatformMXBean(RuntimeMXBean.class);// 获取当前进程的CPU时间(以纳秒为单位)long newProcessCpuTime = osBean.getProcessCpuTime();// 获取当前Java虚拟机的运行时间(以毫秒为单位)long newProcessUpTime = runtimeBean.getUptime();// 获取可用的CPU核心数量int cpuCores = osBean.getAvailableProcessors();// 计算前后两次采集之间进程CPU时间的差值,并转换成毫秒long processCpuTimeDiffInMs = TimeUnit.NANOSECONDS.toMillis(newProcessCpuTime - processCpuTime);// 计算运行时间的差值long processUpTimeDiffInMs = newProcessUpTime - processUpTime;// 将CPU时间差除以运行时间差,然后除以可用CPU核心数。这样得到的结果是每个CPU核心上的平均进程CPU使用率double processCpuUsage = (double) processCpuTimeDiffInMs / processUpTimeDiffInMs / cpuCores;// 更新全局变量存储最新的进程CPU时间和运行时间,以便下一次循环计算时使用processCpuTime = newProcessCpuTime;processUpTime = newProcessUpTime;// 将计算得到的进程CPU使用率与系统CPU使用率进行比较,取较大者作为当前CPU使用率currentCpuUsage = Math.max(processCpuUsage, systemCpuUsage);// 如果当前系统负载(currentLoad)大于预先设定的阈值(SystemRuleManagerif (currentLoad > SystemRuleManager.getSystemLoadThreshold()) {// 调用writeSystemStatusLog()方法,将系统过载信息写入日志中writeSystemStatusLog();}} catch (Throwable e) {RecordLog.warn("[SystemStatusListener] Failed to get system metrics from JMX", e);}}
}
获取频率
public final class SystemRuleManager {// 这种线程池的创建方式值的学习,因为使用了NamedThreadFactory,将线程池里的线程做到见名知意private final static ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1, new NamedThreadFactory("sentinel-system-status-record-task", true));static {// 1s 执行一次scheduler.scheduleAtFixedRate(new SystemStatusListener(), 0, 1, TimeUnit.SECONDS);}
}

参考资料

通关 Sentinel 流量治理框架 - 编程界的小學生

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/762123.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

数据机构-2(顺序表)

线性表 概念 顺序表 示例&#xff1a;创建一个存储学生信息的顺序表 表头&#xff08;Tlen总长度&#xff0c; Clen当前长度&#xff09; 函数 #include <seqlist.c> #include <stdio.h> #include <stdlib.h> #include "seqlist.h" #include &…

Arcade绘制各种各样的文本

""" Example showing how to draw text to the screen.If Python and Arcade are installed, this example can be run from the command line with: python -m arcade.examples.drawing_text """ import arcade# 设置屏幕参数 SCREEN_WIDTH 12…

C/C++ 设置Socket的IP_TOS选项

IP TOS选项是指示IP报文转发的优先级&#xff0c;QOS控制的一种&#xff0c;常规的IP协议TOS都为0&#xff0c;就是普通报文。 设置&#xff1a; IPV4/TOS设置&#xff08;Socket&#xff09;&#xff0c;IPTOS_LOWDELAY 表示FLASH优先级&#xff08;一般用在游戏&#xff0c;…

[GYCTF2020]Ezsqli ---不会编程的崽

又是sql新题型哦。继续收集&#xff01; 既然知道是sql注入就不墨迹了。初步判断盲注&#xff0c;判断盲注的方发不用说了吧&#xff0c;然后fuzz一下&#xff0c;information被过滤了。再次可以判断为盲注与无列名注入。 管他有列名还是无列名&#xff0c;先找到表。由于info…

Uniapp 的 uni.request传参后端

以下是使用Uniapp的交互数据的两种方式 后端使用Parameter接收数据 后端使用RequestBody接收Json格式数据 后端&#xff1a; CrossOrigin RestController RequestMapping("/user") public class UserController {GetMapping("/login")public String lo…

3D 动画领域的创新突破:仅需文字提示即可创建 3D 动画

一、产品简介 这款产品能够通过简单的文字提示生成高品质的 3D 动画,无需进行视频录制或动作捕捉,为用户提供了极大的便利。 二、产品特色 采用先进的生成式 AI 技术,实现文本到动画的快速转换。支持多种常见的动画输出格式,如.FBX、.GLB 等,满足不同用户的需求。提供丰富…

LINQ常用扩展方法、委托、Lambda、yield

LINQ让数据处理变得简单 文章目录 Where方法Count()方法Any()方法获取一条数据(是否带参数的两种写法)SingleSingleOrDefaultFirstFirstOrDefault 排序多规则排序 限制结果集&#xff0c;获取部分数据聚合函数分组投影匿名类型集合转换查询语法委托Lambda表达式LINQyield retur…

管理的常识--决策如何有效计划控制

决策的目的是为了执行 决策决定你的选择 决策是为了能够执行&#xff0c;而不是追求正确性&#xff1b;或者说决策正确性指的不是决策本身&#xff0c;而是决策得到执行的结果 决策是要解决问题而不是简单做出选择&#xff0c;如果简单做出选择&#xff0c;只是完成了决策的过…

kafka学习笔记01(小滴课堂)

介绍分布式流处理平台kafka快速认知 介绍分布式流处理平台kafka核心概念解释 急速部署-Kafka相关环境准备和安装 Linux环境下Zookeeper和Kafka安装启动 解压两个软件的压缩包&#xff1a; tar -zxvf 启动zk: 去log目录进行查看&#xff1a; 查看一下2181端口是否被占用: 安装…

Python实战:Python虚拟环境(venv)的创建与使用

一、引言 在Python开发中&#xff0c;项目之间的依赖管理是一个常见问题。不同的项目可能需要不同的Python版本或依赖库。为了解决这个问题&#xff0c;我们可以使用Python虚拟环境&#xff08;venv&#xff09;。虚拟环境是一种隔离的Python环境&#xff0c;它允许我们为每个…

十九、网络编程

目录 一、什么是网络编程二、网络编程三要素2.1 IP2.2 InetAddress的使用2.3 端口号2.4 协议 三、UDP通信程序3.1 发送数据3.2 接收数据3.3 练习 四、UDP的三种通信方式五、TCP的通信程序六、三次握手和四次挥手七、练习7.1 TCP通信练习1——多发多收7.2 TCP通信练习2——接收和…

80386 ATT汇编语法

文章目录 gcc的预处理&#xff0c;不进行编译、汇编或链接预处理编译汇编 8.8.2 AT&T语法与英特尔语法8.8.3操作码命名8.8.4寄存器命名8.8.5操作码前缀8.8.6内存引用8.8.7跳转指令的处理8.8.8浮点8.8.9写入16位代码8.8.10笔记 gcc的预处理&#xff0c;不进行编译、汇编或链…

Java疫苗接种管理系统

本系统lw为2024-3-21本人原创&#xff0c;查chong13% 1.3W字&#xff0c;可以直接上交&#xff0c;这并不是乱七八糟的技术文档和项目文档。 4.2 功能结构设计 可视化的疫苗接种管理系统功能结构设计主要包括以下几个模块&#xff1a; 登录注册模块&#xff1a;这个模块负责…

浅浅迈入C++门槛

从今天起&#xff0c;我要开始hello&#xff0c;world。 往后更要做到&#xff0c;拳打数据结构&#xff0c;脚踢Linux。 这就是江湖人的风范。 拼搏百天&#xff0c;我要学希普拉斯普拉斯。 C是在C的基础之上&#xff0c;容纳进去了面向对象编程思想&#xff0c;并增加了许…

vmware中ubuntu虚拟机桥接模式和xshell连接

首先&#xff0c;把虚拟机的虚拟机设置那里改成桥接模式&#xff0c;然后电脑本地用的什么网&#xff0c;就拿对应的网卡在vmware的虚拟网络编辑器那里去改第一个成桥接&#xff0c;添加这个网卡去虚拟 之后去ubuntu右上角把ip设置成手动&#xff0c; ip前三个表示网段的和本…

echarts实践总结(常用二):折线图(特点:渐变、面积区域)

目录 第一章 echarts基本使用 第二章 echarts实践——折线图 效果展示 第一章 echarts基本使用 Echarts常用配置项(详细入门)_echarts配置项手册-CSDN博客 柱状图案例&#xff1a; echarts实践总结(常用一)&#xff1a;柱状图&#xff08;特点&#xff1a;渐变色、点击缩放、…

vue3父子通信、跨层通信

子传父 通过 ref标识 获取真实的 dom对象或者组件实例对象 父组件获取子组件内部属性和方法 顶层组件向任意的底层组件传递数据和方法&#xff0c;实现跨层组件通信 非响应式数据父修改不了子的内容 子组件调用父组件方法

【Markdown】【mermaid】Mermaid 简介:使用Markdown动态创建和修改图表

Mermaid 简介 什么是 Mermaid图表类型1. 流程图 (Flowchart)2. 序列图 (Sequence diagram)3. 甘特图 (Gantt diagram)4. 类图 (Class diagram)5. Git 图 (Git graph)6. 实体关系图 (Entity Relationship Diagram)7. 用户旅程图 (User Journey Diagram)8. 象限图 (Quadrant Char…

【现代C++】范围基于的for循环

现代C中的范围基于的for循环&#xff08;range-based for loop&#xff09;是C11引入的一项特性&#xff0c;旨在简化对容器或范围的迭代过程。这种循环语法不仅使代码更清晰易读&#xff0c;还减少了迭代时的错误。以下是范围基于的for循环的详细介绍&#xff1a; 1. 基本用法…

CTK插件框架学习-源码下载编译(01)

1、编译环境 window11、vs17、Qt5.14.0、cmake3.27.4 2、下载链接 cmake&#xff1a;Index of /files/v3.20 qt&#xff1a;Index of / vs22以前的版本需要登录下载&#xff1a;Visual Studio 较旧的下载 - 2019、2017、2015 和以前的版本 vs22下载&#xff1a;下载 Visu…