当前位置: 首页 > news >正文

dubbo 源码系列之-集群三板斧---负载均衡(二)

在上一课时我们了解了 LoadBalance 接口定义以及 AbstractLoadBalance 抽象类的内容,还详细介绍了 ConsistentHashLoadBalance 以及 RandomLoadBalance 这两个实现类的核心原理和大致实现。本课时我们将继续介绍 LoadBalance 的剩余三个实现。

LeastActiveLoadBalance 最小活跃数

LeastActiveLoadBalance 使用的是最小活跃数负载均衡算法。它认为当前活跃请求数越小的 Provider 节点,剩余的处理能力越多,处理请求的效率也就越高,那么该 Provider 在单位时间内就可以处理更多的请求,所以我们应该优先将请求分配给该 Provider 节点。

LeastActiveLoadBalance 需要配合 ActiveLimitFilter 使用,ActiveLimitFilter 会记录每个接口方法的活跃请求数,在 LeastActiveLoadBalance 进行负载均衡时,只会从活跃请求数最少的 Invoker 集合里挑选 Invoker。

在 LeastActiveLoadBalance 的实现中,首先会选出所有活跃请求数最小的 Invoker 对象,之后的逻辑与 RandomLoadBalance 完全一样,即按照这些 Invoker 对象的权重挑选最终的 Invoker 对象。下面是 LeastActiveLoadBalance.doSelect() 方法的具体实现:

protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {// 初始化Invoker数量int length = invokers.size();// 记录最小的活跃请求数int leastActive = -1;// 记录活跃请求数最小的Invoker集合的个数int leastCount = 0;// 记录活跃请求数最小的Invoker在invokers数组中的下标位置 int[] leastIndexes = new int[length];// 记录活跃请求数最小的Invoker集合中,每个Invoker的权重值int[] weights = new int[length];// 记录活跃请求数最小的Invoker集合中,所有Invoker的权重值之和int totalWeight = 0;// 记录活跃请求数最小的Invoker集合中,第一个Invoker的权重值int firstWeight = 0;// 活跃请求数最小的集合中,所有Invoker的权重值是否相同boolean sameWeight = true;for (int i = 0; i < length; i++) { // 遍历所有Invoker,获取活跃请求数最小的Invoker集合Invoker<T> invoker = invokers.get(i);// 获取该Invoker的活跃请求数int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();// 获取该Invoker的权重int afterWarmup = getWeight(invoker, invocation);weights[i] = afterWarmup;// 比较活跃请求数if (leastActive == -1 || active < leastActive) {// 当前的Invoker是第一个活跃请求数最小的Invoker,则记录如下信息leastActive = active; // 重新记录最小的活跃请求数leastCount = 1; // 重新记录活跃请求数最小的Invoker集合个数leastIndexes[0] = i; // 重新记录InvokertotalWeight = afterWarmup; // 重新记录总权重值firstWeight = afterWarmup; // 该Invoker作为第一个Invoker,记录其权重值sameWeight = true; // 重新记录是否权重值相等} else if (active == leastActive) { // 当前Invoker属于活跃请求数最小的Invoker集合leastIndexes[leastCount++] = i; // 记录该Invoker的下标totalWeight += afterWarmup; // 更新总权重if (sameWeight && afterWarmup != firstWeight) {sameWeight = false; // 更新权重值是否相等}}}// 如果只有一个活跃请求数最小的Invoker对象,直接返回即可if (leastCount == 1) {return invokers.get(leastIndexes[0]);}// 下面按照RandomLoadBalance的逻辑,从活跃请求数最小的Invoker集合中,随机选择一个Invoker对象返回if (!sameWeight && totalWeight > 0) {int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);for (int i = 0; i < leastCount; i++) {int leastIndex = leastIndexes[i];offsetWeight -= weights[leastIndex];if (offsetWeight < 0) {return invokers.get(leastIndex);}}}return invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
}

ActiveLimitFilter 以及底层的 RpcStatus 记录活跃请求数的具体原理,在前面的[第 30 课时]中我们已经详细分析过了,这里不再重复,如果有不清楚的地方,你可以回顾之前课时相关的内容。

RoundRobinLoadBalance 加权轮询

RoundRobinLoadBalance 实现的是加权轮询负载均衡算法。

轮询指的是将请求轮流分配给每个 Provider。例如,有 A、B、C 三个 Provider 节点,按照普通轮询的方式,我们会将第一个请求分配给 Provider A,将第二个请求分配给 Provider B,第三个请求分配给 Provider C,第四个请求再次分配给 Provider A……如此循环往复。

轮询是一种无状态负载均衡算法,实现简单,适用于集群中所有 Provider 节点性能相近的场景。 但现实情况中就很难保证这一点了,因为很容易出现集群中性能最好和最差的 Provider 节点处理同样流量的情况,这就可能导致性能差的 Provider 节点各方面资源非常紧张,甚至无法及时响应了,但是性能好的 Provider 节点的各方面资源使用还较为空闲。这时我们可以通过加权轮询的方式,降低分配到性能较差的 Provider 节点的流量。

加权之后,分配给每个 Provider 节点的流量比会接近或等于它们的权重比。例如,Provider 节点 A、B、C 权重比为 5:1:1,那么在 7 次请求中,节点 A 将收到 5 次请求,节点 B 会收到 1 次请求,节点 C 则会收到 1 次请求。

在 Dubbo 2.6.4 版本及之前,RoundRobinLoadBalance 的实现存在一些问题,例如,选择 Invoker 的性能问题、负载均衡时不够平滑等。在 Dubbo 2.6.5 版本之后,这些问题都得到了修复,所以这里我们就来介绍最新的 RoundRobinLoadBalance 实现。

每个 Provider 节点有两个权重:一个权重是配置的 weight,该值在负载均衡的过程中不会变化;另一个权重是 currentWeight,该值会在负载均衡的过程中动态调整,初始值为 0。

当有新的请求进来时,

  1. RoundRobinLoadBalance 会遍历 Invoker 列表,并用对应的 currentWeight 加上其配置的权重。
  2. 遍历完成后,再找到最大的 currentWeight,将其减去权重总和,然后返回相应的 Invoker 对象。

下面我们通过一个示例说明 RoundRobinLoadBalance 的执行流程,这里我们依旧假设 A、B、C 三个节点的权重比例为 5:1:1。

在这里插入图片描述

  1. 处理第一个请求,currentWeight 数组中的权重与配置的 weight 相加,即从 [0, 0, 0] 变为 [5, 1, 1]。接下来,从中选择权重最大的 Invoker 作为结果,即节点 A。最后,将节点 A 的 currentWeight 值减去 totalWeight 值,最终得到 currentWeight 数组为 [-2, 1, 1]。
  2. 处理第二个请求,currentWeight 数组中的权重与配置的 weight 相加,即从 [-2, 1, 1] 变为 [3, 2, 2]。接下来,从中选择权重最大的 Invoker 作为结果,即节点 A。最后,将节点 A 的 currentWeight 值减去 totalWeight 值,最终得到 currentWeight 数组为 [-4, 2, 2]。
  3. 处理第三个请求,currentWeight 数组中的权重与配置的 weight 相加,即从 [-4, 2, 2] 变为 [1, 3, 3]。接下来,从中选择权重最大的 Invoker 作为结果,即节点 B。最后,将节点 B 的 currentWeight 值减去 totalWeight 值,最终得到 currentWeight 数组为 [1, -4, 3]。
  4. 处理第四个请求,currentWeight 数组中的权重与配置的 weight 相加,即从 [1, -4, 3] 变为 [6, -3, 4]。接下来,从中选择权重最大的 Invoker 作为结果,即节点 A。最后,将节点 A 的 currentWeight 值减去 totalWeight 值,最终得到 currentWeight 数组为 [-1, -3, 4]。
  5. 处理第五个请求,currentWeight 数组中的权重与配置的 weight 相加,即从 [-1, -3, 4] 变为 [4, -2, 5]。接下来,从中选择权重最大的 Invoker 作为结果,即节点 C。最后,将节点 C 的 currentWeight 值减去 totalWeight 值,最终得到 currentWeight 数组为 [4, -2, -2]。
  6. 处理第六个请求,currentWeight 数组中的权重与配置的 weight 相加,即从 [4, -2, -2] 变为 [9, -1, -1]。接下来,从中选择权重最大的 Invoker 作为结果,即节点 A。最后,将节点 A 的 currentWeight 值减去 totalWeight 值,最终得到 currentWeight 数组为 [2, -1, -1]。
  7. 处理第七个请求,currentWeight 数组中的权重与配置的 weight 相加,即从 [2, -1, -1] 变为 [7, 0, 0]。接下来,从中选择权重最大的 Invoker 作为结果,即节点 A。最后,将节点 A 的 currentWeight 值减去 totalWeight 值,最终得到 currentWeight 数组为 [0, 0, 0]。

到此为止,一个轮询的周期就结束了。

而在 Dubbo 2.6.4 版本中,上面示例的一次轮询结果是 [A, A, A, A, A, B, C],也就是说前 5 个请求会全部都落到 A 这个节点上。这将会使节点 A 在短时间内接收大量的请求,压力陡增,而节点 B 和节点 C 此时没有收到任何请求,处于完全空闲的状态,这种“瞬间分配不平衡”的情况也就是前面提到的“不平滑问题”。

在 RoundRobinLoadBalance 中,我们为每个 Invoker 对象创建了一个对应的 WeightedRoundRobin 对象,用来记录配置的权重(weight 字段)以及随每次负载均衡算法执行变化的 current 权重(current 字段)。

了解了 WeightedRoundRobin 这个内部类后,我们再来看 RoundRobinLoadBalance.doSelect() 方法的具体实现:

protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();// 获取整个Invoker列表对应的WeightedRoundRobin映射表,如果为空,则创建一个新的WeightedRoundRobin映射表ConcurrentMap<String, WeightedRoundRobin> map = methodWeightMap.computeIfAbsent(key, k -> new ConcurrentHashMap<>());int totalWeight = 0;long maxCurrent = Long.MIN_VALUE;long now = System.currentTimeMillis(); // 获取当前时间Invoker<T> selectedInvoker = null;WeightedRoundRobin selectedWRR = null;for (Invoker<T> invoker : invokers) {String identifyString = invoker.getUrl().toIdentityString();int weight = getWeight(invoker, invocation);// 检测当前Invoker是否有相应的WeightedRoundRobin对象,没有则进行创建WeightedRoundRobin weightedRoundRobin = map.computeIfAbsent(identifyString, k -> {WeightedRoundRobin wrr = new WeightedRoundRobin();wrr.setWeight(weight);return wrr;});// 检测Invoker权重是否发生了变化,若发生变化,则更新WeightedRoundRobin的weight字段if (weight != weightedRoundRobin.getWeight()) {weightedRoundRobin.setWeight(weight);}// 让currentWeight加上配置的Weightlong cur = weightedRoundRobin.increaseCurrent();//  设置lastUpdate字段weightedRoundRobin.setLastUpdate(now);// 寻找具有最大currentWeight的Invoker,以及Invoker对应的WeightedRoundRobinif (cur > maxCurrent) {maxCurrent = cur;selectedInvoker = invoker;selectedWRR = weightedRoundRobin;}totalWeight += weight; // 计算权重总和}if (invokers.size() != map.size()) {map.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);}if (selectedInvoker != null) {// 用currentWeight减去totalWeightselectedWRR.sel(totalWeight);// 返回选中的Invoker对象return selectedInvoker;}return invokers.get(0);
}

ShortestResponseLoadBalance 最短响应时间

ShortestResponseLoadBalance 是Dubbo 2.7 版本之后新增加的一个 LoadBalance 实现类。它实现了最短响应时间的负载均衡算法,也就是从多个 Provider 节点中选出调用成功的且响应时间最短的 Provider 节点,不过满足该条件的 Provider 节点可能有多个,所以还要再使用随机算法进行一次选择,得到最终要调用的 Provider 节点。

了解了 ShortestResponseLoadBalance 的核心原理之后,我们一起来看 ShortestResponseLoadBalance.doSelect() 方法的核心实现,如下所示:

protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {// 记录Invoker集合的数量int length = invokers.size();// 用于记录所有Invoker集合中最短响应时间long shortestResponse = Long.MAX_VALUE;// 具有相同最短响应时间的Invoker个数int shortestCount = 0;// 存放所有最短响应时间的Invoker的下标int[] shortestIndexes = new int[length];// 存储每个Invoker的权重int[] weights = new int[length];// 存储权重总和int totalWeight = 0;// 记录第一个Invoker对象的权重int firstWeight = 0;// 最短响应时间Invoker集合中的Invoker权重是否相同boolean sameWeight = true;for (int i = 0; i < length; i++) {Invoker<T> invoker = invokers.get(i);RpcStatus rpcStatus = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName());// 获取调用成功的平均时间,具体计算方式是:调用成功的请求数总数对应的总耗时 / 调用成功的请求数总数 = 成功调用的平均时间// RpcStatus 的内容在前面课时已经介绍过了,这里不再重复long succeededAverageElapsed = rpcStatus.getSucceededAverageElapsed();// 获取的是该Provider当前的活跃请求数,也就是当前正在处理的请求数int active = rpcStatus.getActive();// 计算一个处理新请求的预估值,也就是如果当前请求发给这个Provider,大概耗时多久处理完成long estimateResponse = succeededAverageElapsed * active;// 计算该Invoker的权重(主要是处理预热)int afterWarmup = getWeight(invoker, invocation);weights[i] = afterWarmup;if (estimateResponse < shortestResponse) { // 第一次找到Invoker集合中最短响应耗时的Invoker对象,记录其相关信息shortestResponse = estimateResponse;shortestCount = 1;shortestIndexes[0] = i;totalWeight = afterWarmup;firstWeight = afterWarmup;sameWeight = true;} else if (estimateResponse == shortestResponse) {// 出现多个耗时最短的Invoker对象shortestIndexes[shortestCount++] = i;totalWeight += afterWarmup;if (sameWeight && i > 0&& afterWarmup != firstWeight) {sameWeight = false;}}}if (shortestCount == 1) {return invokers.get(shortestIndexes[0]);}// 如果耗时最短的所有Invoker对象的权重不相同,则通过加权随机负载均衡的方式选择一个Invoker返回if (!sameWeight && totalWeight > 0) {int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);for (int i = 0; i < shortestCount; i++) {int shortestIndex = shortestIndexes[i];offsetWeight -= weights[shortestIndex];if (offsetWeight < 0) {return invokers.get(shortestIndex);}}}// 如果耗时最短的所有Invoker对象的权重相同,则随机返回一个return invokers.get(shortestIndexes[ThreadLocalRandom.current().nextInt(shortestCount)]);
}

总结

我们紧接上一课时介绍了 LoadBalance 接口的剩余三个实现。

我们首先介绍了

  • LeastActiveLoadBalance 实现,它使用最小活跃数负载均衡算法,选择当前请求最少的 Provider 节点处理最新的请求;
  • 接下来介绍了 RoundRobinLoadBalance 实现,它使用加权轮询负载均衡算法,弥补了单纯的轮询负载均衡算法导致的问题,同时随着 Dubbo 版本的升级,也将其自身不够平滑的问题优化掉了;
  • 最后介绍了 ShortestResponseLoadBalance 实现,它会从响应时间最短的 Provider 节点中选择一个 Provider 节点来处理新请求。

相关文章:

dubbo 源码系列之-集群三板斧---负载均衡(二)

在上一课时我们了解了 LoadBalance 接口定义以及 AbstractLoadBalance 抽象类的内容&#xff0c;还详细介绍了 ConsistentHashLoadBalance 以及 RandomLoadBalance 这两个实现类的核心原理和大致实现。本课时我们将继续介绍 LoadBalance 的剩余三个实现。 LeastActiveLoadBala…...

【一周一篇小题解】洛谷P1035级数求和

还是很简单的…… 首先&#xff0c;输入k&#xff0c;再定义一个双精度浮点数s&#xff0c;不停的循环 for(i1;;i) 每次循环加上1.0/i s1.0/i; 如果大于k就停止循环&#xff0c;并输出&#xff0c;也可以输出后直接结束程序 写法一&#xff1a; if(s>k) { printf("%…...

2024-03-26 AIGC-大模型学习路线

摘要: 2024-03-26 AIGC-大模型学习路线 大模型学习路线 建议先从主流的Llama开始&#xff0c;然后选用中文的Qwen/Baichuan/ChatGLM&#xff0c;先快速上手体验prompt工程&#xff0c;然后再学习其架构&#xff0c;跑微调脚本 如果要深入学习&#xff0c;建议再按以下步骤&am…...

QGraphicsView的使用,view坐标,scene坐标,item坐标

Graphics View绘图构架 QGraphicsScene&#xff08;场景&#xff09;&#xff1a;可以管理多个图形项QGraphicsItem&#xff08;图形项&#xff09;&#xff1a;也就是图元&#xff0c;支持鼠标事件响应。QGraphicsView&#xff08;视图&#xff09;&#xff1a;关联场景可以让…...

from_pretrained 做了啥

transformers的三个核心抽象类是Config, Tokenizer和Model&#xff0c;这些类根据模型种类的不同&#xff0c;派生出一系列的子类。构造这些派生类的对象也很简单&#xff0c;transformers为这三个类都提供了自动类型&#xff0c;即AutoConfig, AutoTokenizer和AutoModel。三个…...

2024/03/27(C++·day3)

一、思维导图 二、完成下面类 代码 #include <cstring> #include <iostream>using namespace std;class myString { private:char *str; // 记录C风格的字符串int size; // 记录字符串的实际长度public:// 无参构造函数myString() : size(10){str new char[si…...

Multimodal Chain-of-Thought Reasoning in Language Models阅读笔记

论文&#xff08;2023年&#xff09;链接&#xff1a;https://arxiv.org/pdf/2302.00923.pdf GitHub项目链接&#xff1a;GitHub - amazon-science/mm-cot: Official implementation for "Multimodal Chain-of-Thought Reasoning in Language Models" (stay tuned a…...

C语言例4-15:从键盘输入一个整数,求其绝对值并输出。

代码如下&#xff1a; //从键盘输入一个整数&#xff0c;求其绝对值并输出。 #include<stdio.h> int main(void) {int n;printf("输出一个整数&#xff1a; \n");scanf("%d",&n); //从键盘输入一个整数保存至变量nif(n<0) //…...

【Linux】调试器-gdb的使用说明(调试器的配置,指令说明,调试过程说明)

目录 00.背景 01.安装 02.生成调试信息 03.调试过程 00.背景 在软件开发中&#xff0c;通常会为程序构建两种不同的版本&#xff1a;Debug模式和Release模式。它们之间的区别主要在于优化级别、调试信息、错误检查等方面&#xff1a; 1.Debug 模式&#xff1a; 优化级别低…...

Oracle AI Vector Search Multi-Vector Similarity Search 即多向量相似度检索学习笔记

Oracle AI Vector Search Multi-Vector Similarity Search 即多向量相似度检索学习笔记 0. 什么是多向量相似度检索1. 多向量相似度检索的示例 SQL2. 执行多向量相似度检索3. 分区行限制子句的完整语法 0. 什么是多向量相似度检索 多向量相似度检索涉及通过使用称为分区的分组…...

白板手推公式性质 AR模型 时间序列分析

白板手推公式性质 AR模型 时间序列分析 视频讲解&#xff1a;https://www.bilibili.com/video/BV1D1421S76v/?spm_id_from.dynamic.content.click&vd_source6e452cd7908a2d9b382932f345476fd1 B站对应视频讲解(白板手推公式性质 AR模型 时间序列分析)...

零基础学python之高级编程(6)---Python中进程的Queue 和进程锁,以及进程池的创建 (包含详细注释代码)

Python中进程的Queue 和进程锁,以及进程池的创建 文章目录 Python中进程的Queue 和进程锁,以及进程池的创建前言一、进程间同步通信(Queue)二、进程锁&#xff08;Lock&#xff09;三、创建进程池Poorpool 类方法: End! 前言 大家好,上一篇文章,我们初步接触了进程的概念及其应…...

184. 部门工资最高的员工

文章目录 题意思路代码 题意 题目链接 查出每个部门最高工资 思路 子查询group by 代码 select b.name as Department,a.name as Employee,salary from Employee as a left joinDepartment as b ona.departmentId b.id where(a.departmentId, salary) in(select departme…...

插值表达式、Vue指令、指令补充

vue上手步骤 <body><!-- vue2语法 --><!-- 1.准备容器&#xff1a;一会vue就会把数据展示到这里 --><div id"app"><!-- 4.使用{{ }}即可显示数据 &#xff0c;{{}}就是插值表达式--><p>姓名&#xff1a;{{uname}}</p><…...

qiankun实现基座、子应用样式隔离

目录 qiankun 实现主应用与子应用样式隔离使用CSS-in-JS来实现样式隔离react-jssstyled-components qiankun 实现主应用与子应用样式隔离 qiankun 之中默认的样式隔离是针对子应用与子应用之间的。至于主应用的样式会影响到子应用&#xff0c;若需要&#xff0c;则需要配置进行…...

C语言从入门到实战----数据在内存中的存储

1. 整数在内存中的存储 在讲解操作符的时候&#xff0c;我们就讲过了下⾯的内容&#xff1a; 整数的2进制表⽰⽅法有三种&#xff0c;即 原码、反码和补码 有符号的整数&#xff0c;三种表⽰⽅法均有符号位和数值位两部分&#xff0c;符号位都是⽤0表⽰“正”&#xff0c;⽤…...

接口关联和requests库

一、接口关联 postman的接口 postman的接口关联配置&#xff1a;js代码&#xff0c;重点在于思路。 // 定义jsonData这个变量 接受登录接口的返回结果 var jsonData JSON.parse(responseBody); // 从返回结果里提取token/id值&#xff0c;并赋值给token/id变量值作为环境变…...

Python编程基础 001 开篇:为什么要学习编程

Python编程基础 001 开篇:为什么要学习编程 一、什么是程序&#xff0c;什么是编程二、学习编程对青少年的价值&#xff08;一&#xff09;未来社会的需要&#xff08;二&#xff09;学习对现青少年现的现阶段的直接影响 三、学习编程从什么时候开始&#xff08;一&#xff09;…...

AQS源码分析

前言 AbstractQueuedSynchronizer是抽象同步队列&#xff0c;其是实现同步机器的基础组件&#xff0c;并发包中的锁的底层就是使用AQS实现的。AQS中 维护了一个volatile int state&#xff08;代表共享资源&#xff09;和一个FIFO线程等待队列&#xff08;多线程争用资源被阻塞…...

应对Locked勒索病毒威胁:你的数据安全准备好了吗?

导言&#xff1a; .Locked勒索病毒&#xff0c;作为一种新型的恶意软件&#xff0c;已经在全球范围内引起了广泛的关注。这种病毒通过加密受害者的文件&#xff0c;并要求支付赎金以获取解密密钥&#xff0c;从而实现对受害者的勒索。本文旨在深入解析.Locked勒索病毒的特点、…...

OpenClaw性能调优:Qwen3-32B镜像的批处理与并发控制

OpenClaw性能调优&#xff1a;Qwen3-32B镜像的批处理与并发控制 1. 为什么需要性能调优 当我第一次在RTX4090D上部署Qwen3-32B模型并接入OpenClaw时&#xff0c;本以为24GB显存足以应对各种任务。但现实很快给了我一记重拳——当我尝试批量处理100个文档时&#xff0c;系统不…...

将Autoresearch转化为通用技能

我是一名技术作家。我每天在文档仓库、Markdown 文件、API 参考、风格指南和 SEO 审计中度过。我不训练语言模型。我不写 CUDA 内核。但当 Andrej Karpathy 发布了他的 autoresearch 时&#xff0c;我无法停止思考它。 这个想法太简单了&#xff0c;事后看来似乎很明显&#x…...

Kazam vs OBS:Ubuntu 24.04 屏幕录制工具对比与选择指南

Kazam vs OBS&#xff1a;Ubuntu 24.04 屏幕录制工具深度评测与实战选择 在数字内容创作爆发的时代&#xff0c;屏幕录制已成为游戏实况、在线教学、产品演示的标配技能。对于Ubuntu 24.04用户而言&#xff0c;Kazam和OBS Studio这两款开源工具常被拿来比较——前者以轻量简洁著…...

3分钟快速上手:免费高效的Elasticsearch可视化工具Elasticvue终极指南

3分钟快速上手&#xff1a;免费高效的Elasticsearch可视化工具Elasticvue终极指南 【免费下载链接】elasticvue Elasticsearch gui for the browser 项目地址: https://gitcode.com/gh_mirrors/el/elasticvue 你是否曾经为复杂的Elasticsearch集群管理而烦恼&#xff1f…...

RGBLEDBlender:嵌入式RGB LED色彩混合与动态控制框架

1. RGBLEDBlender 库深度解析&#xff1a;面向嵌入式系统的 RGB 色彩混合与动态控制框架RGBLEDBlender 是一个轻量级、面向硬件的 RGB LED 色彩混合库&#xff0c;专为资源受限的微控制器平台&#xff08;尤其是 Arduino 生态&#xff09;设计。该库由 Erik Sikich 于 2016 年 …...

水库调度员必看:动态规划在月度发电计划中的5个避坑指南

水库调度员实战指南&#xff1a;动态规划在月度发电计划中的5个关键避坑策略 在水利工程领域&#xff0c;水库调度是一项集科学性、技术性和艺术性于一体的复杂工作。作为水库调度员&#xff0c;我们每天都在与时间、水量和电力需求进行着精妙的博弈。而动态规划作为一种强大的…...

离网逆变器下垂控制实战:从公式推导到MATLAB仿真(附资源下载)

离网逆变器下垂控制实战&#xff1a;从公式推导到MATLAB仿真 在新能源发电系统中&#xff0c;离网逆变器的稳定运行至关重要。传统电压电流双闭环控制虽然简单直接&#xff0c;但在面对复杂负载变化时&#xff0c;往往会出现电压跌落、频率失稳等问题。下垂控制技术通过模拟同…...

不止于公式:用国民技术N32G45x定时器实现精准时间片调度(附代码)

不止于公式&#xff1a;用国民技术N32G45x定时器实现精准时间片调度&#xff08;附代码&#xff09; 在嵌入式系统开发中&#xff0c;定时器是最基础也最强大的外设之一。对于国民技术N32G45x系列微控制器而言&#xff0c;其丰富的定时器资源&#xff08;TIM2/3/4等&#xff09…...

三步掌握BepInEx插件框架:零基础也能懂的Unity游戏扩展指南

三步掌握BepInEx插件框架&#xff1a;零基础也能懂的Unity游戏扩展指南 【免费下载链接】BepInEx Unity / XNA game patcher and plugin framework 项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx BepInEx作为Unity/XNA游戏的插件框架&#xff0c;为开发者和…...

给渗透新手的保姆级指南:用Kali和MSF搞定VulnHub经典靶机DC-1

Kali Linux渗透测试实战&#xff1a;从零攻破VulnHub DC-1靶机 环境准备与靶机配置 在开始渗透测试之前&#xff0c;确保你已经准备好以下工具和环境。Kali Linux作为渗透测试的标准发行版&#xff0c;集成了我们所需的所有工具。DC-1是Vulnhub上一个专为渗透测试练习设计的靶机…...