中间件(二)dubbo负载均衡介绍

一、负载均衡概述

支持轮询、随机、一致性hash和最小活跃数等。

1、轮询

① sequences:内部的序列计数器
② 服务器接口方法权重一样:(sequences+1)%服务器的数量=(决定调用)哪个服务器的服务。
③ 服务器接口方法权重不一样:找到最大权重(权重数)%(sequences+1),然后找出权重比该取模后的值大服务器列表,最后进行【①】所述。

2、随机

统计服务器上该接口的方法权重总和,然后对这个总和随机nextInt一下,看生成的随机数落到哪个段内,就调用哪个服务器上的该服务。

3、一致性hash

保证了同样的请求(参数)将会落到同一台服务器上。

4、最小活跃数

每个接口和接口方法都对应一个RpcStatus,记录它们的活跃数、失败等相关统计信息,调用时活跃数+1,调用结束时活跃数-1,所以活跃值越大,表明该提供者服务器的该接口方法耗时越长,而消费能力强的提供者接口往往活跃值很低。最少活跃负载均衡保证了“慢”提供者能接收到更少的服务器调用。

二、负载均衡策略配置

1、多注册中心集群负载均衡

在这里插入图片描述

2、Cluster Invoker

支持的选址策略如下(dubbo2.7.5+ 版本,具体使用请参见文档)

2-1、指定优先级

<!-- 来自 preferred=“true” 注册中心的地址将被优先选择,
只有该中心无可用地址时才 Fallback 到其他注册中心 -->
<dubbo:registry address="zookeeper://${zookeeper.address1}" preferred="true" />

2-2、同zone优先

<!-- 选址时会和流量中的 zone key 做匹配,流量会优先派发到相同 zone 的地址 -->
<dubbo:registry address="zookeeper://${zookeeper.address1}" zone="beijing" />

2-3、权重轮询

<!-- 来自北京和上海集群的地址,将以 10:1 的比例来分配流量 -->
<dubbo:registry id="beijing" address="zookeeper://${zookeeper.address1}" weight=”100“ />
<dubbo:registry id="shanghai" address="zookeeper://${zookeeper.address2}" weight=”10“ />

三、负载均衡解读

(1)负载均衡:AbstractClusterInvoker.invoke(final Invocation invocation)方法

@Override
public Result invoke(final Invocation invocation) throws RpcException {//...... 省略代码 List<Invoker<T>> invokers = list(invocation);LoadBalance loadbalance = initLoadBalance(invokers, invocation);RpcUtils.attachInvocationIdIfAsync(getUrl(), invocation);return doInvoke(invocation, invokers, loadbalance);
}
/** 获取负载均衡的实现方法,未配置时默认random */
protected LoadBalance initLoadBalance(List<Invoker<T>> invokers, Invocation invocation) {if (CollectionUtils.isNotEmpty(invokers)) {return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(invokers.get(0).getUrl().getMethodParameter(RpcUtils.getMethodName(invocation), LOADBALANCE_KEY, DEFAULT_LOADBALANCE));} else {return ExtensionLoader.getExtensionLoader(LoadBalance.class).getExtension(DEFAULT_LOADBALANCE);}
}

(2)实现入口:AbstractClusterInvoker.doSelect(…)

① 在dubbo中,所有负载均衡均继承 AbstractLoadBalance 类,该类实现了LoadBalance接口,并封装了一些公共的逻辑。

/** 1. LoadBalance.java接口 */
@SPI(RandomLoadBalance.NAME)
public interface LoadBalance {/*** select one invoker in list.** @param invokers   invokers.* @param url        refer url* @param invocation invocation.* @return selected invoker.*/@Adaptive("loadbalance")<T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) throws RpcException;
}
/** 2. AbstractLoadBalance.java 抽象类 */
@Override
public <T> Invoker<T> select(List<Invoker<T>> invokers, URL url, Invocation invocation) {if (CollectionUtils.isEmpty(invokers)) {return null;}// 如果invokers列表中仅一个Invoker,直接返回即可,无需进行负载均衡if (invokers.size() == 1) {return invokers.get(0);}// 调用doSelect方法进行负载均衡,该方法为抽象方法,由子类实现return doSelect(invokers, url, invocation);
}protected abstract <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation);

/** 2-1. 公共方法,权重计算逻辑 */

protected int getWeight(Invoker<?> invoker, Invocation invocation) {int weight;URL url = invoker.getUrl();// Multiple registry scenario, load balance among multiple registries.if (REGISTRY_SERVICE_REFERENCE_PATH.equals(url.getServiceInterface())) {weight = url.getParameter(REGISTRY_KEY + "." + WEIGHT_KEY, DEFAULT_WEIGHT);} else {// 从url中获取权重 weight配置值weight = url.getMethodParameter(invocation.getMethodName(), WEIGHT_KEY, DEFAULT_WEIGHT);if (weight > 0) {// 获取服务提供者启动时间戳long timestamp = invoker.getUrl().getParameter(TIMESTAMP_KEY, 0L);if (timestamp > 0L) {// 计算服务提供者运行时长long uptime = System.currentTimeMillis() - timestamp;if (uptime < 0) {return 1; // 未启动直接返回权重为1}// 获取服务预热时间,默认为10分钟int warmup = invoker.getUrl().getParameter(WARMUP_KEY, DEFAULT_WARMUP);// 如果服务运行时间小于预热时间,则重新计算服务权重,即降级if (uptime > 0 && uptime < warmup) {weight = calculateWarmupWeight((int)uptime, warmup, weight);}}}}return Math.max(weight, 0);
}

// 2-2. 重新计算服务权重

static int calculateWarmupWeight(int uptime, int warmup, int weight) {// 计算权重,下面代码逻辑上形似于 (uptime / warmup) * weight// 随着服务运行时间 uptime 增大,权重计算值 ww 会慢慢接近配置值 weightint ww = (int) ( uptime / ((float) warmup / weight));return ww < 1 ? 1 : (Math.min(ww, weight));
}

注:
select 方法的逻辑比较简单,首先会检测 invokers 集合的合法性,然后再检测 invokers 集合元素数量。如果只包含一个 Invoker,直接返回该 Inovker 即可。如果包含多个 Invoker,此时需要通过负载均衡算法选择一个 Invoker。具体的负载均衡算法由子类实现。
权重的计算主要用于保证当服务运行时长小于服务预热时间时,对服务进行降权,避免让服务在启动之初就处于高负载状态。服务预热是一个优化手段,与此类似的还有 JVM 预热。主要目的是让服务启动后“低功率”运行一段时间,使其效率慢慢提升至最佳状态。
配置方式(默认100):dubbo.provider.weight=300dubbo.provider.weight=300

② RandomLoadBalance 加权随机负载均衡
算法思路:根据权重比,随机选择哪台服务器,如:servers=[A,B,C],weights = [5, 3, 2],权重和为10,调用A的次数约有50%,B有30%,C有20%。

@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {// Number of invokersint length = invokers.size();// 判断是否需要权重负载均衡if (!needWeightLoadBalance(invokers,invocation)){return invokers.get(ThreadLocalRandom.current().nextInt(length));}// Every invoker has the same weight?boolean sameWeight = true;// the maxWeight of every invokers, the minWeight = 0 or the maxWeight of the last invokerint[] weights = new int[length];// The sum of weightsint totalWeight = 0;// ① 计算总权重,totalWeight;② 检测每个服务提供者的权重是否相同for (int i = 0; i < length; i++) {int weight = getWeight(invokers.get(i), invocation);// SumtotalWeight += weight;// save for later use// 如果权重分别为5,3,2,则weights[0]=5,weights[1]=8,weights[2]=10weights[i] = totalWeight;// 判断每个服务权重是否相同,如果不相同则sameWeight置为falseif (sameWeight && totalWeight != weight * (i + 1)) {sameWeight = false;}}// 各提供者服务权重不一样时,计算随机数落在哪个区间上if (totalWeight > 0 && !sameWeight) {// If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.// 随机获取一个[0, totalWeight]区间内的随机的数字int offset = ThreadLocalRandom.current().nextInt(totalWeight);// Return a invoker based on the random value.for (int i = 0; i < length; i++) {// weights[0]=5,offset=[0, 5)// weights[1]=8,offset=[5, 8)// weights[2]=10,offset=[8, 10)if (offset < weights[i]) {return invokers.get(i);}}}// If all invokers have the same weight value or totalWeight=0, return evenly.// 如果所有服务提供者权重值相同,此时直接随机返回一个即可return invokers.get(ThreadLocalRandom.current().nextInt(length));
}

注:RandomLoadBalance的算法比较简单,多次请求后,能够按照权重进行“均匀“分配。调用次数少时,可能产生的随机数比较集中,此缺点并不严重,可以忽略。它是一个高效的负载均衡实现,因此Dubbo选择它作为缺省实现。

③ LeastActiveLoadBalance 加权最小活跃数负载均衡
活跃调用数越小,表明该服务提供者效率越高,单位时间内可处理更多的请求。此时应优先将请求分配给该服务提供者。
在具体实现中,每个服务提供者对应一个活跃数 active。初始情况下,所有服务提供者活跃数均为0。每收到一个请求,活跃数加1,完成请求后则将活跃数减1。
除了最小活跃数,LeastActiveLoadBalance 在实现上还引入了权重值。所以准确的来说,LeastActiveLoadBalance 是基于加权最小活跃数算法实现的。

@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {// Number of invokersint length = invokers.size();// The least active value of all invokers// 最小活跃数int leastActive = -1;// The number of invokers having the same least active value (leastActive)// 具有相同“最小活跃数”的服务提供者int leastCount = 0;// The index of invokers having the same least active value (leastActive)// leastIndexes 用于记录具有相同“最小活跃数”的 Invoker 在 invokers 列表中的下标信息int[] leastIndexes = new int[length];// the weight of every invokersint[] weights = new int[length];// The sum of the warmup weights of all the least active invokersint totalWeight = 0;// The weight of the first least active invoker// 第一个最小活跃数的 Invoker 权重值,用于与其他具有相同最小活跃数的 Invoker 的权重进行对比,// 以检测是否“所有具有相同最小活跃数的 Invoker 的权重”均相等int firstWeight = 0;// Every least active invoker has the same weight value?// 表示各服务的权重是否相同boolean sameWeight = true;// Filter out all the least active invokersfor (int i = 0; i < length; i++) {Invoker<T> invoker = invokers.get(i);// Get the active number of the invoker// 获取invoker对应的活跃数int active = RpcStatus.getStatus(invoker.getUrl(), invocation.getMethodName()).getActive();// Get the weight of the invoker's configuration. The default value is 100.// 获取权重int afterWarmup = getWeight(invoker, invocation);// save for later useweights[i] = afterWarmup;// If it is the first invoker or the active number of the invoker is less than the current least active number// 发现更小的活跃数,重新开始if (leastActive == -1 || active < leastActive) {// Reset the active number of the current invoker to the least active number// 使用当前活跃数 active 更新最小活跃数 leastActiveleastActive = active;// Reset the number of least active invokersleastCount = 1;// Put the first least active invoker first in leastIndexes// 记录当前下标值到 leastIndexes 中leastIndexes[0] = i;// Reset totalWeighttotalWeight = afterWarmup;// Record the weight the first least active invokerfirstWeight = afterWarmup;// Each invoke has the same weight (only one invoker here)sameWeight = true;// If current invoker's active value equals with leaseActive, then accumulating.// 当前 Invoker 的活跃数 active 与最小活跃数 leastActive 相同} else if (active == leastActive) {// Record the index of the least active invoker in leastIndexes orderleastIndexes[leastCount++] = i;// Accumulate the total weight of the least active invoker// 累加权重totalWeight += afterWarmup;// If every invoker has the same weight?// 检测当前 Invoker 的权重与 firstWeight 是否相等,不相等则将 sameWeight 置为 falseif (sameWeight && afterWarmup != firstWeight) {sameWeight = false;}}}// Choose an invoker from all the least active invokers// 1. 当只有一个 Invoker 具有最小活跃数,此时直接返回该 Invoker 即可if (leastCount == 1) {// If we got exactly one invoker having the least active value, return this invoker directly.return invokers.get(leastIndexes[0]);}// 2. 有多个 Invoker 具有相同的最小活跃数,但它们之间的权重不同if (!sameWeight && totalWeight > 0) {// If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on // totalWeight.// 随机生成一个 [0, totalWeight) 之间的数字int offsetWeight = ThreadLocalRandom.current().nextInt(totalWeight);// Return a invoker based on the random value.for (int i = 0; i < leastCount; i++) {// 获取权重数组的下标int leastIndex = leastIndexes[i];// 随机权重 - 具有最小活跃数的 Invoker 的权重值offsetWeight -= weights[leastIndex];if (offsetWeight < 0) { // 与RandomLoadBalance一致,权重越大调用的次数越多return invokers.get(leastIndex);}}}// If all invokers have the same weight value or totalWeight=0, return evenly.// 如果权重相同或权重为0时,随机返回一个 Invokerreturn invokers.get(leastIndexes[ThreadLocalRandom.current().nextInt(leastCount)]);
}

④ ConsistentHashLoadBalance
cache-1、cache-2、cache-3、cache-4分别为不同的节点
根据IP或者其他信息为缓存节点生成一个hash,并将这个hash投射到[0,2^32 - 1] 的圆环上。当有查询或写入请求时,则为缓存项的key生成一个hash值。然后查找第一个大于或等于该hash值的缓存节点,并到这个节点中查询或写入缓存项。
如果当前节点挂了,则在下一次查询或写入缓存时,为缓存项查找另一个大于或其hash值的缓存节点即可。
如下图,每个节点在圆环上占据一个位置,如果缓存项的key的hash值小于缓存节点hash值,则到该缓存节点中存储或读取缓存项。
比如下面绿色点对应的缓存项将会被存储到 cache-2 节点中。由于 cache-3 挂了,原本应该存到该节点中的缓存项最终会存储到 cache-4 节点中。
请添加图片描述
⑤ RoundRobinLoadBalance
轮询:是指将请求轮流分配给每台服务器。
例如:有三台服务器A、B、C,我们将第一个请求分配给A服务器,第二个请求分配给B服务器,第三个请求分配给C服务器,第四个请求再次分配给A服务器,如此循环,这个过程叫做轮询(轮询是一种无状态负载均衡算法)。适用于每台服务器性能相近的场景下。
轮询加权:对每台性能不一样的服务器进行加权处理,如服务器A、B、C的权重分别为5:2:1时,在8次请求中,服务器A将收到5次请求、B收到2次请求、C收到一次请求(请求次数越多,每台服务器得到的请求数,接近服务器的权重比)。

@Override
protected <T> Invoker<T> doSelect(List<Invoker<T>> invokers, URL url, Invocation invocation) {// key = [group/]path[:version].methodName;注:path = com.jyt.*.service.接口名String key = invokers.get(0).getUrl().getServiceKey() + "." + invocation.getMethodName();// 获取key对应值,如果key的值不存在,则将第二个参数的返回值存入并返回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) {// dubbo://11.1.1.109:21001/com.jyt.*.service.类名String identifyString = invoker.getUrl().toIdentityString();// 获取当前服务提供者的权重int weight = getWeight(invoker, invocation);// 根据identifyString获取当前提供者对应的权重,如果不存在则使用第二个参数返回值,并返回WeightedRoundRobin weightedRoundRobin = map.computeIfAbsent(identifyString, k -> {WeightedRoundRobin wrr = new WeightedRoundRobin();wrr.setWeight(weight);return wrr;});// 如果提供者的权重被修改了,则更新weightedRoundRobin的权重值if (weight != weightedRoundRobin.getWeight()) {// weight changedweightedRoundRobin.setWeight(weight);}// current加上weight并获取结果(初始的current为0)long cur = weightedRoundRobin.increaseCurrent();/*** 如果A服务权重weight=500,B权重weight=100时,totalWeight = 600,初始cur=0;服务调用场景* 第一次:A服务器:cur=weight + curA = 500,cur > maxCurrent,所以maxCurrent = curA = 500*        B服务器:cur=weight + curB = 100 <= maxCurrent(500为true);故走A服务器,即curA = curA - 600 = -100** 第二次:A服务器:cur=weight + curA = 400,cur > maxCurrent,所以maxCurrent = curA = 400*        B服务器:cur=weight + curB = 200 <= maxCurrent(400为true);故走A服务器,即curA = curA - 600 = -200** 第三次:A服务器:cur=weight + curA = 300,cur > maxCurrent,所以maxCurrent = curA = 300*        B服务器:cur=weight + curB = 300 <= maxCurrent(300为true);故走A服务器,即curA = curA - 600 = -300** 第四次:A服务器:cur=weight + curA = 200,cur > maxCurrent,所以maxCurrent = curA = 200*        B服务器:cur=weight + curB = 400 <= maxCurrent(200为false);故走B服务器,即curB = curB - 600 = -200** 第五次:A服务器:cur=weight + curA = 700,cur > maxCurrent,所以maxCurrent = curA = 700*        B服务器:cur=weight + curB = -100 <= maxCurrent(700为true);故走A服务器,即curA = curA - 600 = 100** 第六次:A服务器:cur=weight + curA = 600,cur > maxCurrent,所以maxCurrent = curA = 600*        B服务器:cur=weight + curB = 0 <= maxCurrent(600为true);故走A服务器,即curA = curA - 600 = 0*        * ... ... 如此循环:A、A、A、B、A、A*/weightedRoundRobin.setLastUpdate(now);// 判断是否比最大的值大if (cur > maxCurrent) {// 如果大,则将当前服务提供者置为本次服务提供者maxCurrent = cur;selectedInvoker = invoker;selectedWRR = weightedRoundRobin;}// 权重累计totalWeight += weight;}// 当两者大小不一致时,map中可能会存在一些已经下线的服务,本次剔除一些很久节点信息if (invokers.size() != map.size()) {map.entrySet().removeIf(item -> now - item.getValue().getLastUpdate() > RECYCLE_PERIOD);}// 如果存在选择好的提供者,则改变他的current值 - totalWeight;if (selectedInvoker != null) {selectedWRR.sel(totalWeight);return selectedInvoker;}// should not happen herereturn invokers.get(0);
}

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

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

相关文章

预警:传统的QA岗位将被DevOps淘汰

导读在大多数机构或公司里&#xff0c;软件开发过程主要遵循一个或多个开发模型&#xff0c;例如瀑布模型或敏捷模型。在瀑布模型中&#xff0c;测试活动一般都在后期进行。软件开发完成后&#xff0c;缺陷被QA团队找出&#xff0c;然后再被修复。后两个活动不断循环和重复&…

创建KVM虚拟机

文章目录 安装KVM虚拟机环境准备硬件虚拟化添加一块磁盘分区并格式化创建挂载目录并挂载分区上传镜像&#xff1a; virt-manager图形化安装下载virt-manager开始安装 virsh-install命令行安装安装组件使用virt-install安装 virsh管理虚拟机基本命令拓展命令 安装KVM虚拟机 环境…

【福建事业单位-公基-法】02国家基本制度、公民的基本权利和义务 国家机构

【福建事业单位-公基-法】02国家基本制度 一、国家基本制度1.1 自然资源归属1.2 选举制度1.3 民族区域自治制度总结 二、公民的基本权利和义务1.1 权力1.2 义务总结 三、国家机构3.1 全国人民代表大会3.2全国人民代表大会常务委员会3.3 国家主席3.4国务院3.5监察委3.6 人民法院…

设计模式

本文主要介绍设计模式的主要设计原则和常用设计模式。 一、UML画图 1.类图 2.时序图 二、设计模式原则 1.单一职责原则 就是一个方法、一个类只做一件事&#xff1b; 2.开闭原则 就是软件的设计应该对拓展开放&#xff0c;对修改关闭&#xff0c;这在java中体现最明显的就…

Kubernetes二进制部署方案

目录 一、环境准备 2.1、主机配置 2.2、安装 Docker 2.3、生成通信加密证书 2.3.1、生成 CA 证书&#xff08;所有主机操作&#xff09; 2.3.2、生成 Server 证书&#xff08;所有主机&#xff09; 2.3.3、生成 admin 证书(所有主机) 2.3.4、生成 proxy 证书 三、部署 …

Three.js程序化3D城市建模【OpenStreetMap】

对于我在 Howest 的研究项目&#xff0c;我决定构建一个 3D 版本的 Lucas Bebber 的“交互式讲故事的动画地图路径”项目。 我将使用 OSM 中的矢量轮廓来挤出建筑物的形状并将它们添加到 3js 场景中&#xff0c;随后我将对其进行动画处理 推荐&#xff1a;用 NSDT编辑器 快速搭…

VR/AR眼镜方案,MTK联发科平台智能眼镜安卓主板设计方案

随着人工智能在不同领域的逐渐深入&#xff0c;人们对一款产品的需求不再局限于某种单一的功能或单一场景&#xff0c;尤其是在工业医疗等专业领域&#xff0c;加快数字化转型才能实现产业的升级。 AR智能眼镜&#xff0c;是一个可以让现场作业更智能的综合管控设备。采用移动…

jvm内存溢出排查(使用idea自带的内存泄漏分析工具)

文章目录 1.确保生成内存溢出文件2.使用idea自带的内存泄漏分析工具3.具体实验一下 1.确保生成内存溢出文件 想分析堆内存溢出&#xff0c;一定在运行jar包时就写上参数-XX:HeapDumpOnOutOfMemoryError&#xff0c;可以看我之前关于如何运行jar包的文章。若你没有写。可以写上…

SpringBoot 学习(03): 弱语言的注解和SpringBoot注解的异同

弱语言代表&#xff1a;Hyperf&#xff0c;一个基于 PHP Swoole 扩展的常驻内存框架 注解概念的举例说明&#xff1b; 说白了就是&#xff0c;你当领导&#xff0c;破烂事让秘书帮你去安排&#xff0c;你只需要批注一下&#xff0c;例如下周要举办一场活动&#xff0c;秘书将方…

【VBA_选择区域的关键词更改颜色】

Private Sub CommandButtonl_Click() Cells.Font.ColorIndex 1 End Sub Sub Worksheet_SelectionChange(ByVal Target As Range) Dim rng As Range, i As Integer Dim T As String Dim C As Integer For Each rng In Selection T "河北" C 3 i 1 Do While InStr(…

SpringBoot + Vue 前后端分离项目 微人事(九)

职位管理后端接口设计 在controller包里面新建system包&#xff0c;再在system包里面新建basic包&#xff0c;再在basic包里面创建PositionController类&#xff0c;在定义PositionController类的接口的时候&#xff0c;一定要与数据库的menu中的url地址到一致&#xff0c;不然…

javaScript:模板字符串让你忘记字符串拼接

目录 一.前言 二.模板字符串的使用 1.介绍 2.模板字符串 支持换行 模板字符串更适合元素写入 innerHTML模板字符串写法 3.模板字符串中&#xff0c;可以运行表达式 4.模板字符串中可以运行函数 三.总结 语法&#xff1a; 多行字符串&#xff1a; 变量插值&#xff1a; …

SpringBoot统⼀功能处理

前言&#x1f36d; ❤️❤️❤️SSM专栏更新中&#xff0c;各位大佬觉得写得不错&#xff0c;支持一下&#xff0c;感谢了&#xff01;❤️❤️❤️ Spring Spring MVC MyBatis_冷兮雪的博客-CSDN博客 本章是讲Spring Boot 统⼀功能处理模块&#xff0c;也是 AOP 的实战环节&…

学习 Linux 系统路线图

在计算机科学领域&#xff0c;Linux 操作系统以其稳定性、灵活性和卓越性能而受到广泛欢迎。要真正掌握 Linux 系统&#xff0c;我们需要深入了解其关键组成部分&#xff0c;包括系统、内存、进程、网络和存储等模块。让我们深入探索这些模块&#xff0c;以建立起对 Linux 系统…

归并排序:从二路到多路

前言 我们所熟知的快速排序和归并排序都是非常优秀的排序算法。 但是快速排序和归并排序的一个区别就是&#xff1a;快速排序是一种内部排序&#xff0c;而归并排序是一种外部排序。 简单理解归并排序&#xff1a;递归地拆分&#xff0c;回溯过程中&#xff0c;将排序结果进…

深度学习在MRI运动校正中的应用综述

运动是MRI中的主要挑战之一。由于MR信号是在频率空间中获取的&#xff0c;因此除了其他MR成像伪影之外&#xff0c;成像对象的任何运动都会导致重建图像中产生伪影。深度学习被提出用于重建过程的几个阶段的运动校正。广泛的MR采集序列、感兴趣的解剖结构和病理学以及运动模式&…

vue3使用vuex

VUEX官方文档,可以学习详细,这篇笔记是写vue2升级vue3后使用vuex,或者忘记如何使用vuex做状态管理的情况 vueX状态管理 Vue 3 与 Vue 2 有很多不同之处&#xff0c;但 Vuex 的核心概念——State、Getters、Mutations、Actions 和 Modules——保持基本一致。Vuex 4 是为 Vue 3 …

部署工业物联网可以选择哪些通信方案?

部署工业物联网有诸多意义&#xff0c;诸如提升生产效率&#xff0c;降低管理成本&#xff0c;保障生产品质稳定&#xff0c;应对长期从业劳动力变化趋势等。针对不同行业、场景&#xff0c;工业物联网需要选择不同的通信方案&#xff0c;以达到成本和效益的最佳平衡。本篇就简…

drawio----输出pdf为图片大小无空白(图片插入论文)

自己在写论文插入图片时为了让论文图片放大不模糊&#xff0c;啥方法都试了&#xff0c;最后摸索出来这个。 自己手动画图的时候导出pdf总会出现自己的图片很小&#xff0c;pdf的白边很大如下如所示&#xff0c;插入论文的时候后虽然放大不会模糊&#xff0c;但是白边很大会显…

开源了一套基于springboot+vue+uniapp的商城,包含分类、sku、商户管理、分销、会员、适合企业或个人二次开发

RuoYi-Mall-JAVA商城-电商系统简介 开源了一套基于若依框架&#xff0c;SringBoot2MybatisPlusSpringSecurityjwtredisVueUniapp的前后端分离的商城系统&#xff0c; 包含分类、sku、商户管理、分销、会员、适合企业或个人二次开发。 前端采用Vue、Element UI&#xff08;ant…