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.rhkb.cn/news/281742.html

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

相关文章

Springboot笔记(web开启)-08

有一些日志什么的后续我会补充 1.使用springboot: 创建SpringBoot应用&#xff0c;选中我们需要的模块&#xff1b;SpringBoot已经默认将这些场景配置好了&#xff0c;只需要在配置文件中指定少量配置就可以运行起来自己编写业务代码&#xff1b; 2.SpringBoot对静态资源的映…

c语言基础笔记(1)进制转换以及++a,a++,取地址和解引用

一进制转换 OCT - 八进制 DEC - 十进制 HEX - 十六进制 0520&#xff0c;表示八进制 0x520表示16进制 unsigned 无符号&#xff0c;只有正的 signed 有正有负数 char默认是signed 类型 #include <stdio.h>int main(void) { //字符转换成数字char a 5;int a1 a- 4…

HarmonyOS入门学习

HarmonyOS入门学习 前言快速入门ArkTS组件基础组件Image组件Text组件TextInput 文本输入框Buttonslider 滑动组件 页面布局循环控制ForEach循环创建组件 List自定义组件创建自定义组件Builder 自定义函数 状态管理Prop和LinkProvide和ConsumeObjectLink和Observed ArkUI页面路由…

从后端获取文件数据并导出

导出文件的公共方法 export const download (res, tools) > {const { message, hide } tools;const fileReader: any new FileReader();console.log(fileReader-res>>>, res);fileReader.onload (e) > {if (res?.data?.type application/json) {try {co…

数字孪生与智慧城市:重塑城市生活的新模式

随着信息技术的迅猛发展&#xff0c;数字孪生作为一种新兴的技术理念&#xff0c;正在逐渐改变城市建设和管理的传统模式。智慧城市作为数字孪生技术应用的重要领域&#xff0c;正在以其独特的优势和潜力&#xff0c;重塑着城市生活的方方面面。本文将从数字孪生的概念、智慧城…

Java-SSM电影购票系统

Java-SSM电影购票系统 1.服务承诺&#xff1a; 包安装运行&#xff0c;如有需要欢迎联系&#xff08;VX:yuanchengruanjian&#xff09;。 2.项目所用框架: 前端:JSP、layui、bootstrap等。 后端:SSM,即Spring、SpringMvc、Mybatis等。 3.项目功能点: 3-1.后端功能: 1.用户管…

解决GNURadio自定义C++ OOT块-导入块时报错问题

文章目录 前言一、问题描述二、解决方法1、安装依赖2、配置环境变量3、重新编译及安装三、结果1、添加结果2、运行结果前言 本文记录在 GNURadio 自定义 C++ OOT 块后导入块时报错 AttributeError: module myModule has no attribute multDivSelect。 一、问题描述 参考官方教…

作品展示ETL

1、ETL 作业定义、作业导入、控件拖拽、执行、监控、稽核、告警、报告导出、定时设定 欧洲某国电信系统数据割接作业定义中文页面&#xff08;作业顶层&#xff0c;可切英文&#xff0c;按F1弹当前页面帮助&#xff09; 涉及文件拆分、文件到mysql、库到库、数据清洗、数据转…

银行量子金融系统应用架构设计

量子金融&#xff08;即Financial-Quantum&#xff0c;简称Fin-Q&#xff09;&#xff0c;特指量子科技在金融行业中的应用。 目前&#xff0c;量子科技中以量子保密通信、量子随机数和量子计算发展进度较快&#xff0c;取得了诸多阶段性重大技术突破和商用成果&#xff0c;这…

【FLOOD FILL专题】【蓝桥杯备考训练】:扫雷、动态网格、走迷宫、画图、山峰和山谷【已更新完成】

目录 1、扫雷&#xff08;Google Kickstart2014 Round C Problem A&#xff09; 2、动态网格&#xff08;Google Kickstart2015 Round D Problem A&#xff09; 3、走迷宫&#xff08;模板&#xff09; 4、画图&#xff08;第六次CCF计算机软件能力认证&#xff09; 5、山…

【蓝桥杯】RMQ(Range Minimum/Maximum Query)

一.概述 RMQ问题&#xff0c;是求区间最大值或最小值&#xff0c;即范围最值问题。 暴力解法是对每个询问区间循环求解&#xff0c;设区间长度n&#xff0c;询问次数m&#xff0c;则复杂度是O ( nm )。 一般还可以使用线段树求解&#xff0c;复杂度是O(mlogn)。 但还有一种…

Postgresql数据库入门简介

Postgresql入门 1.Postgresql数据库简介 PostgresQL是一个功能强大的开源数据库系统。经过长达15年以上的积极开发和不断改进&#xff0c;PostgreSQL已在可靠性、稳定性、数据一致性等获得了业内极高的声誉。目前PostgreSql可以运行在所有主流操作系统上&#xff0c;包括Linux…

会员项目定价卡css3特效

会员项目定价卡css3特效&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面 下载地址 会员项目定价卡css3特效代码

【爬虫】web自动化和接口自动化

专栏文章索引&#xff1a;爬虫 目录 一、介绍 二、推荐 1.接口自动化 2.Web自动化 一、介绍 爬虫技术一般可以分为两种类型&#xff1a;接口自动化和web自动化。下面是它们的简要介绍&#xff1a; 1.接口自动化 接口自动化技术的主要目的是通过模拟HTTP请求来实现自动化…

Zama:链上隐私新标准

1. 引言 揭示 Web3 中全同态加密的潜在用例&#xff0c;并深入研究 Zama 的四种主要开源产品&#xff1a; TFHE-rsConcreteConcrete MLfhEVM 众所周知&#xff0c;在当今时代&#xff0c;数据隐私问题与互联网诞生以来一样普遍。仅 Yahoo!、Equifax 和 Marriott 的数据泄露就…

java动态规划学习笔记

学习笔记目录&#xff0c;这里记录个大纲&#xff0c;详情点链接 背包问题 01背包问题综述 01背包问题&#xff08;二维数组&#xff09;https://blog.csdn.net/m0_73065928/article/details/136794406?spm1001.2014.3001.5501 01背包问题&#xff08;滚动数组&#xff09…

LeetCode 热题 100 | 堆(一)

目录 1 什么是堆排序 1.1 什么是堆 1.2 如何构建堆 1.3 举例说明 2 215. 数组中的第 K 个最大元素 2.1 子树大根化 2.2 遍历所有子树 2.3 弹出栈顶元素 2.4 完整代码 菜鸟做题&#xff0c;语言是 C 1 什么是堆排序 1.1 什么是堆 堆的定义和分类&#xff…

ECharts5 概念篇1

图表容器及大小 初始化 在 HTML 中定义有宽度和高度的父容器&#xff08;推荐&#xff09; 通常来说&#xff0c;需要在 HTML 中先定义一个 <div> 节点&#xff0c;并且通过 CSS 使得该节点具有宽度和高度。初始化的时候&#xff0c;传入该节点&#xff0c;图表的大小默认…

海外客户获取难?海外云手机助力电商引流!

海外电商面临的市场竞争激烈&#xff0c;如何在海外市场获客成为了摆在许多卖家面前的难题。而在这个问题的解决方案中&#xff0c;海外云手机崭露头角&#xff0c;成为助力电商引流的新利器。 在当前市场中&#xff0c;云手机主要用于游戏挂机&#xff0c;但其潜力在海外电商领…

四、C语言中的数组:如何输入与输出二维数组(数组,完)

本章的学习内容如下 四、C语言中的数组&#xff1a;数组的创建与初始化四、C语言中的数组&#xff1a;数组的输入与元素个数C语言—第6次作业—十道代码题掌握一维数组四、C语言中的数组&#xff1a;二维数组 1.二维数组的输入与输出 当我们输入一维数组时需要一个循环来遍历…