中间件(二)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 会慢慢接近配置值 weight
    int 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 invokers
    int 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 invoker
    int[] weights = new int[length];
    // The sum of weights
    int totalWeight = 0;
    // ① 计算总权重,totalWeight;② 检测每个服务提供者的权重是否相同
    for (int i = 0; i < length; i++) {
        int weight = getWeight(invokers.get(i), invocation);
        // Sum
        totalWeight += weight;
        // save for later use
        // 如果权重分别为5,3,2,则weights[0]=5,weights[1]=8,weights[2]=10
        weights[i] = totalWeight;
        // 判断每个服务权重是否相同,如果不相同则sameWeight置为false
        if (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 invokers
    int 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 invokers
    int[] weights = new int[length];
    // The sum of the warmup weights of all the least active invokers
    int 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 invokers
    for (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 use
        weights[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 更新最小活跃数 leastActive
            leastActive = active;
            // Reset the number of least active invokers
            leastCount = 1;
            // Put the first least active invoker first in leastIndexes
            // 记录当前下标值到 leastIndexes 中
            leastIndexes[0] = i;
            // Reset totalWeight
            totalWeight = afterWarmup;
            // Record the weight the first least active invoker
            firstWeight = 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 order
            leastIndexes[leastCount++] = i;
            // Accumulate the total weight of the least active invoker
            // 累加权重
            totalWeight += afterWarmup;
            // If every invoker has the same weight?
            // 检测当前 Invoker 的权重与 firstWeight 是否相等,不相等则将 sameWeight 置为 false
            if (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时,随机返回一个 Invoker
    return 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 changed
            weightedRoundRobin.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 here
    return invokers.get(0);
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

MAUI+Blazor:windows 打包踩坑

文章目录 前言MSIX安装文件如何发布选择Windows平台旁加载自定义签名版本号安装 总结 前言 最近打算研究一下MAUIBlazor&#xff0c;争取在今年年底之前彻底搞懂MAUIBlazor的安装模式&#xff0c; MSIX安装文件 Windows 4种安装程序格式MSI&#xff0c;EXE、AppX和MSIX优缺点…

Unity zSpace 开发

文章目录 1.下载 zSpace 开发环境1.1 zCore Unity Package1.2 zView Unity Package 2. 导入工程3. 发布设置4.功能实现4.1 用触控笔来实现对模型的拖拽&#xff1a; 5. 后续更新 1.下载 zSpace 开发环境 官网地址 1.1 zCore Unity Package zSpace 开发核心必须 1.2 zView …

4.文件操作和IO

文章目录 1.认识文件1.1树型结构组织 和 目录1.2文件路径&#xff08;Path&#xff09;1.3其他知识 2.Java 中操作文件2.1File 概述2.1.1属性2.1.2构造方法2.1.3方法 2.2代码示例2.2.1示例1-get 系列的特点和差异2.2.2示例2-普通文件的创建、删除2.2.3示例3-普通文件的删除2.2.…

Jenkins-CICD-python/Java包升级与回退

Jenkins- CICD流水线 python/Java代码升级与回退 1、执行思路 1.1、代码升级 jenkins上点击 upgrade和 代码版本号 --${tag} jenkins 推送 代码 和 执行脚本 到目标服务器/opt目录下 执行命令 sh run.sh 代码名称 版本号 upgrade 版本号 来自jenkins的 构建参数中的 标签…

【LNMP(分布式)】

目录 一、LNMP是什么 二、实际步骤 1.启用虚拟机 1.1 启动三台虚拟机分别命名为nginx&#xff0c;mysql&#xff0c;php 1.2 分别配置基础环境 1.3 测试外网连通性 2.更新源 3.安装nginx并配置 3.1 下载nginx源码包并安装 3.2 配置nginx 4.安装mysql并配置 4.1 安装…

MySQL中事务特性以及隔离机制

目录 一、什么是事务 二、事务特性——即ACID特性 三、事务的隔离级别 1、脏读 2、不可重复读 3、幻读 Read uncommitted&#xff1a; Read committed: Repeatable read: Serializable&#xff1a; 一、什么是事务 事务&#xff08;Transaction&#xff09;——一个最…

Maven 基础之依赖管理、范围、传递、冲突

文章目录 关于依赖管理坐标和 mvnrepository 网站pom.xml 中"引"包 依赖范围依赖传递依赖冲突 关于依赖管理 坐标和 mvnrepository 网站 在 maven 中通过『坐标』概念来确定一个唯一确定的 jar 包。坐标的组成部分有&#xff1a; 元素说明<groupId>定义当前…

Nacos权限认证

写在前面&#xff1a;各位看到此博客的小伙伴&#xff0c;如有不对的地方请及时通过私信我或者评论此博客的方式指出&#xff0c;以免误人子弟。多谢&#xff01;如果我的博客对你有帮助&#xff0c;欢迎进行评论✏️✏️、点赞&#x1f44d;&#x1f44d;、收藏⭐️⭐️&#…

Uniapp当中使用腾讯位置路线规划插件保姆教学

首先我们在使用腾讯地图插件之前我们需要先做几点准备 1&#xff1a;我们需要在腾讯地图位置服务当中注册账号以及在控制台当中创建应用和创建key 这里在创建应用当中应用类型一定要选出行类型&#xff0c;否则后期可能会出现问题。 我们创建完应用之后&#xff0c;点击创建…

NPCon:AI模型技术与应用峰会北京站 (参会感受)

8月12日&#xff0c;我有幸参加了在北京皇家格兰云天大酒店举行的“AI模型技术与应用峰会”。 这次会议邀请了很多技术大咖&#xff0c;他们围绕&#xff1a; 六大论点 大模型涌现&#xff0c;如何部署训练架构与算力芯片 LLM 应用技术栈与Agent全景解析 视觉GPU推理服务部署 …

python命令行参数argparse的简单使用

1、终端中执行脚本程序 pycharm的终端中执行 python xxx.py命令行中执行程序 2、获取命令行输入的参数 import sysprint(sys.argv) 3.专门处理命令行的library&#xff1a;argparse 添加optional arguments参数&#xff1a;默认是可选的&#xff0c;意味着可以不用填写 p…

VR时代真的到来了?

业界对苹果的期待是&#xff0c;打造一台真正颠覆性的&#xff0c;给头显设备奠定发展逻辑底座的产品&#xff0c;而实际上&#xff0c;苹果只是发布了一台更强大的头显。 大众希望苹果回答的问题是“我为什么需要一台AR或者VR产品&#xff1f;”&#xff0c;但苹果回答的是“…

history记录日期时间和日志记录操作

history命令能查看到操作日期和时间的配置方法&#xff1a; 1&#xff09;在/etc/profile文件中添加一行&#xff1a; export HISTTIMEFORMAT"%F %T whoami " 2&#xff09;保存后&#xff0c;执行加载命令&#xff1a; source /etc/profile 3&#xff09;然后检…

Linux MQTT智能家居项目(智能家居界面布局)

文章目录 前言一、创建工程项目二、界面布局准备工作三、正式界面布局总结 前言 一、创建工程项目 1.选择工程名称和项目保存路径 2.选择QWidget 3.添加保存图片的资源文件&#xff1a; 在工程目录下添加Icon文件夹保存图片&#xff1a; 将文件放入目录中&#xff1a; …

对任意类型数都可以排序的函数:qsort函数

之前我们学习过冒泡排序&#xff1a; int main() {int arr[] { 9,7,8,6,5,4,3,2,1,0 };int sz sizeof(arr)/sizeof(arr[0]);int i 0;for (i 0; i < sz-1; i) {int j 0;for (j 0; j < sz-1-i; j) {if (arr[j] > arr[j 1]){int temp 0;temp arr[j];arr[j] ar…

前后端分离------后端创建笔记(04)前后端对接

本文章转载于【SpringBootVue】全网最简单但实用的前后端分离项目实战笔记 - 前端_大菜007的博客-CSDN博客 仅用于学习和讨论&#xff0c;如有侵权请联系 源码&#xff1a;https://gitee.com/green_vegetables/x-admin-project.git 素材&#xff1a;https://pan.baidu.com/s/…

【HarmonyOS】API9沉浸式状态栏

对于沉浸式状态栏&#xff0c;在之前API8 FA模型开发中可以通过在config.json配置主题的方式实现应用的沉浸式体验&#xff0c;在最新的API9 Stage模型中系统提供了沉浸式窗口的示例&#xff08;管理应用窗口&#xff08;Stage模型&#xff09;-窗口管理-开发-HarmonyOS应用开发…

数据结构入门指南:二叉树

目录 文章目录 前言 1. 树的概念及结构 1.1 树的概念 1.2 树的基础概念 1.3 树的表示 1.4 树的应用 2. 二叉树 2.1 二叉树的概念 2.2 二叉树的遍历 前言 在计算机科学中&#xff0c;数据结构是解决问题的关键。而二叉树作为最基本、最常用的数据结构之一&#xff0c;不仅在算法…

【网络】传输层——TCP(滑动窗口流量控制拥塞控制延迟应答捎带应答)

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《网络》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 上篇文章对TCP可靠性机制讲解了一部分&#xff0c;这篇文章接着继续讲解。 &#x1f3a8;滑动窗口 在…

【LeetCode】543.二叉树的直径

题目 给你一棵二叉树的根节点&#xff0c;返回该树的 直径 。 二叉树的 直径 是指树中任意两个节点之间最长路径的 长度 。这条路径可能经过也可能不经过根节点 root 。 两节点之间路径的 长度 由它们之间边数表示。 示例 1&#xff1a; 输入&#xff1a;root [1,2,3,4,5]…