客户端动态降级系统

news2024/11/19 11:21:54

6600909dc06a4dd9a102933d90ac2fc2.jpeg

41f2186e9ce2e22a8bdd74c6fb1005ea.gif

本文字数:4576

预计阅读时间:20分钟

c716a54f88dbc8768458a0efdc5e857f.png

01

背景

无论是iOS还是Android系统的设备,在线上运行时受硬件、网络环境、代码质量等多方面因素影响,可能会导致性能问题,这一类问题有些在开发阶段是发现不了的。如何在线上始终为用户提供一个相对顺畅的用户体验,是客户端开发需要考虑的一个问题。

02

服务降级、熔断

服务端有降级机制和熔断机制,在设计客户端降级系统时可以参照服务端现有方案。例如发生性能问题或网络拥堵的情况,需要减少设备和网络的负担,等恢复后再进行策略升级。

服务端降级机制,当服务端出现整体负载较大,或因为特殊原因出现数据错误,则会触发降级。不同的情况对应不同的降级策略。例如数据原因导致的,可以不去读DB数据库,直接返回缓存数据。从用户的角度来看,可能是数据更新不及时,但可以正常显示。

服务端熔断机制,熔断机制是比降级更严重的情况,当服务端中某个微服务不可用,或响应时间过长,则会触发熔断,不再调用这个服务。从用户的角度来看,可能是头像不能显示,或者页面部分模版未显示,购物车商品结算不能使用优惠券等。

03

方案简述

首先,我们需要捋清楚,客户端需要处理的问题都有哪些。我将其分为两类,性能和网速,性能又可以细化为CPU、内存、电量三类,这三类都会对App的运行造成影响。同样的,我们不能直接把性能分为好和坏两种,而是需要通过枚举的方式,将其细化为不同等级。

这里以iOS系统为例,我们需要对iOS设备CPU、内存、电量、网速进行实时监控,可以设定一个合理的间隔区间。在发生前面的性能问题时,通过对不同类型的问题进行阈值计算,从而得出对应的等级。如果级别发生变化,则通过通知的方式,告诉业务方降级或升级。

当发生降级时,业务方进行对应的降级处理,例如降低网络请求的图片尺寸。通过业务降级处理,降低系统性能消耗,让CPU、内存逐步恢复到正常区间,再进行业务升级,恢复原有业务处理规则。

通过上述方式,来保证发生性能或网络问题时,用户依然可以较为流畅的使用App,并且App内功能的正常使用不受影响。

04

整体设计

动态降级系统的设计,主要分为三个部分,职责划分如下。

DynamicLevelManager:调用monitordecision完成分级计算,当级别发生变化时,通过通知的方式告知业务方。

DynamicLevelMonitor:监控关键性能指标,由manager定时调用。

DynamicLevelDecision:由manager将收集到的性能指标交给decisiondesicion对于指标进行统一计算,并决定性能级别,并返回给manager

8ae50dffd87b0dfb0e108129c6c6457b.png

下面是脱敏后的伪代码,主要是表达清楚设计思路。 demo 代码也可以直接跑起来,如有需要可以直接 copy 拿去用。

05

DynamicLevelManager

DynamicLevelManager为动态降级系统的核心类,后面都称为manager,当App启动时通过openLevelAnalyze方法注册监听,从而开启一个由dispatch_source_t实现的loop,每隔1.5秒执行一次,执行时会触发dispatch_source_set_event_handler的回调方法。dispatch_source_t由手机硬件时钟触发,不受主线程卡顿影响,监听相对精确很多。

/// 开启动态降级监控系统
- (void)openLevelAnalyze {
    self.sourceHandle = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, dispatch_get_global_queue(0, 0));
    dispatch_source_set_timer(self.sourceHandle, dispatch_time(DISPATCH_TIME_NOW, 0), 1.5 * NSEC_PER_SEC, 0);
    dispatch_source_set_event_handler(self.sourceHandle, ^{
        /// 计算综合性能级别
        CGFloat cpuUsageValue = [[DynamicLevelMonitor sharedInstance] cpuUsageForApp];
        NSInteger memoryUsageValue = [[DynamicLevelMonitor sharedInstance] useMemoryForApp];
        CGFloat batteryUsageValue = [[DynamicLevelMonitor sharedInstance] batteryUsageForApp];
        [[DynamicLevelDecision sharedInstance] calculatePerformanceLevelWithMemoryValue:memoryUsageValue
                                                                               cpuValue:cpuUsageValue
                                                                           batteryValue:batteryUsageValue
                                                                        completionBlock:^(MemoryUsageLevel memoryLevel, CPUUsageLevel cpuLevel, BatteryUsageLevel batteryLevel, MultiplePerformanceLevel performanceLevel) {
            /// 判断级别是否发生变化,发送性能降级或恢复原有等级的通知
            if (performanceLevel != self.currentPerformanceLevel) {
                [self postPerformanceNotifiWithPerformanceLevel:performanceLevel
                                                    memoryLevel:memoryLevel
                                                       cpuLevel:cpuLevel
                                                   batteryLevel:batteryLevel];
            }
        }];
        
        /// 计算网络性能级别
        CGFloat networkSpeed = [[QUICManager shareQUICManager] currentNetworkSpeed];
        [[DynamicLevelDecision sharedInstance] calculateNetworkLevelWithNetworkSpeed:networkSpeed completionBlock:^(NetworkSpeedLevel speedLevel) {
            /// 判断级别是否发生变化,发送网络降级或恢复原有等级的通知
            if (speedLevel != self.currentNetworkSpeedLevel) {
                [self postPerformanceNotifiWithNetworkSpeedLevel:speedLevel];
            }
        }];
    });
    dispatch_resume(self.sourceHandle);
}

- (void)closeLevelAnalyze {
    dispatch_source_cancel(self.sourceHandle);
}

/// 发送性能降级或恢复原有等级的通知
- (void)postPerformanceNotifiWithPerformanceLevel:(MultiplePerformanceLevel)performanceLevel
                                      memoryLevel:(MemoryUsageLevel)memoryLevel
                                         cpuLevel:(CPUUsageLevel)cpuLevel
                                     batteryLevel:(BatteryUsageLevel)batteryLevel {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"PerformanceLevelChanged"
                                                        object:nil
                                                      userInfo:@{@"performanceLevel": @(performanceLevel),
                                                                 @"memoryLevel": @(memoryLevel),
                                                                 @"cpuLevel": @(cpuLevel),
                                                                 @"batteryLevel": @(batteryLevel)}];
}

/// 发送网络降级或恢复原有等级的通知
- (void)postPerformanceNotifiWithNetworkSpeedLevel:(NetworkSpeedLevel)networkSpeedLevel {
    [[NSNotificationCenter defaultCenter] postNotificationName:@"NetworkSpeedLevelChanged"
                                                        object:nil
                                                      userInfo:@{@"networkSpeedLevel": @(networkSpeedLevel)}];
}

manager对外界提供的消息回调分为两类,一个是由CPU、内存、电量,综合计算出的性能分级performanceLevel,另一个是网速分级networkSpeedLevel

5.1 performanceLevel

handler方法中会调用monitorcpuUsageForApp方法获取CPU的使用率,取值范围是0-1,当CPU发生超频,也有超过1的情况。调用monitoruseMemoryForApp方法获取内存使用率,取值范围是0-1。调用monitorbatteryUsageForApp方法获取剩余电量,取值范围是0-100

获取这些信息后,调用decisioncalculatePerformanceLevel方法,将信息交由decision进行综合计算,计算后返回结果为四个值。

1、performanceLevel:综合性能分级

2、memoryLevel:内存占用率分级

3、cpuLevelCPU使用率分级

4、batteryLevel:电量使用分级

这里的核心就是performanceLevel综合分级,类型为MultiplePerformanceLevel,这是根据内存、电量、CPU综合计算出来的结果。上述的四个值通过枚举定义,具体定义如下。

/// 综合性能枚举
typedef NS_ENUM(NSUInteger, MultiplePerformanceLevel) {
    MultiplePerformanceLevelNormal,
    MultiplePerformanceLevelLow,
    MultiplePerformanceLevelVeryLow,
};

/// cpu使用率枚举,overclock表示cpu已超频
typedef NS_ENUM(NSUInteger, CPUUsageLevel) {
    CPUUsageLevelLow,
    CPUUsageLevelHigh,
    CPUUsageLevelOverclock,
};

/// 内存使用级别枚举
typedef NS_ENUM(NSUInteger, MemoryUsageLevel) {
    MemoryUsageLevelLow,
    MemoryUsageLevelMiddle,
    MemoryUsageLevelHigh,
};

/// 电量使用枚举,high表示使用较多,电量剩余1%
typedef NS_ENUM(NSUInteger, BatteryUsageLevel) {
    BatteryUsageLevelLow,
    BatteryUsageLevelMiddle,
    BatteryUsageLevelHigh,
};

拿到这些性能level后,会判断performanceLevel是否发生变化,如果低于当前level,则发生降级。如果高于当前level,则表示性能恢复。随后会调用NSNotificationCenter以通知的形式进行消息通知,通知名为PerformanceLevelChanged,并这四个分级参数传递过去。如果level没有发生改变,则不会发出消息通知。

5.2 speedLevel

另一个是网速分级,这个指标并没有归类于性能分级中,因为和性能分级并不是一类。

handler方法中会调用网络库QUICManagercurrentNetworkSpeed方法,获得当前网速,单位是kb每秒。这里的QUICManager是公司自研的网络库,提供当前实时网速。

拿到网速数据后,会调用decisioncalculateNetworkLevel方法,交给decision进行计算。decision会返回一个speedLevel当前网速级别,其类型是NetworkSpeedLevel,分为三个级别。

/// 当前网速枚举
typedef NS_ENUM(NSUInteger, NetworkSpeedLevel) {
    NetworkSpeedLevelNormal,
    NetworkSpeedLevelLow,
    NetworkSpeedLevelVeryLow,
};

拿到这些信息后,会判断speedLevel是否发生改变,如果低于当前level,则表示网速发生劣化。如果高于当前level,则表示网速恢复。随后会调用NSNotificationCenter以通知的形式进行消息通知,通知名为NetworkSpeedLevelChanged,并将speedLevel参数传递过去。如果level没有发生改变,则不会发出消息通知。

06

DynamicLevelDecision

Decision负责接收manager传入的数据信息,返回对应的性能级别。在计算时,会先对传入的参数进行计算,计算出对应单个性能参数的level分级,再计算performanceLevel分级。

/// 进行综合性能计算
- (void)calculatePerformanceLevelWithMemoryValue:(NSInteger)memoryValue
                                        cpuValue:(CGFloat)cpuValue
                                    batteryValue:(CGFloat)batteryValue
                                 completionBlock:(DynamicPerformanceLevelBlock)completionBlock {
    MemoryUsageLevel memoryLevel = [self calculateMemoryUsageLevelWithMemoryValue:memoryValue];
    CPUUsageLevel cpuLevel = [self calculateCPUUsageLevelWithCpuValue:cpuValue];
    BatteryUsageLevel batteryLevel = [self calculateBatteryUsageLevelWithBatteryValue:batteryValue];
    
    MultiplePerformanceLevel performanceLevel = MultiplePerformanceLevelNormal;
    if (batteryLevel == BatteryUsageLevelHigh) {
        performanceLevel = MultiplePerformanceLevelVeryLow;
    }
    else if (cpuLevel == CPUUsageLevelOverclock && memoryLevel == MemoryUsageLevelHigh) {
        performanceLevel = MultiplePerformanceLevelVeryLow;
    }
    else if (batteryLevel >= 1 && memoryLevel >= 1) {
        performanceLevel = MultiplePerformanceLevelLow;
    }
    else if (batteryLevel >= 1 && cpuLevel >= 1) {
        performanceLevel = MultiplePerformanceLevelLow;
    }
    else if (memoryLevel >= 1 && cpuLevel >= 1) {
        performanceLevel = MultiplePerformanceLevelLow;
    }
    
    if (completionBlock) {
        completionBlock(memoryLevel, cpuLevel, batteryLevel, performanceLevel);
    }
}

/// 进行网速级别计算
- (void)calculateNetworkLevelWithNetworkSpeed:(CGFloat)networkSpeed
                              completionBlock:(DynamicNetworkSpeedLevelBlock)completionBlock {
    [self.networkSpeedArray addObject:@(networkSpeed)];
    if (self.networkSpeedArray.count > 5) {
        [self.networkSpeedArray removeObjectsInRange:NSMakeRange(0, self.networkSpeedArray.count - 5)];
    }
    
    __block NSInteger middleCount = 0;
    __block NSInteger highCount = 0;
    [self.networkSpeedArray enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.floatValue <= 200) {
            middleCount++;
        }
        if (obj.floatValue <= 50) {
            highCount++;
        }
    }];
    
    NetworkSpeedLevel networkThreshold = NetworkSpeedLevelNormal;
    if (highCount >= 3) {
        networkThreshold = NetworkSpeedLevelVeryLow;
    } else if (middleCount >= 3) {
        networkThreshold = NetworkSpeedLevelLow;
    }
    
    if (completionBlock) {
        completionBlock(networkThreshold);
    }
}

/// 计算内存使用级别
- (MemoryUsageLevel)calculateMemoryUsageLevelWithMemoryValue:(NSInteger)memoryValue {
    [self.memoryUsageArray addObject:@(memoryValue)];
    if (self.memoryUsageArray.count > 5) {
        [self.memoryUsageArray removeObjectsInRange:NSMakeRange(0, self.memoryUsageArray.count - 5)];
    }
    
    __block NSInteger middleCount = 0;
    __block NSInteger highCount = 0;
    [self.memoryUsageArray enumerateObjectsUsingBlock:^(NSNumber * _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
        if (obj.floatValue > 0.45) {
            highCount++;
        }
        if (obj.floatValue > 0.4) {
            middleCount++;
        }
    }];
    
    MemoryUsageLevel memoryThreshold = MemoryUsageLevelLow;
    if (highCount >= 3) {
        memoryThreshold = MemoryUsageLevelHigh;
    } else if (middleCount >= 3) {
        memoryThreshold = MemoryUsageLevelMiddle;
    }
    return memoryThreshold;
}

/// 计算CPU使用级别
- (CPUUsageLevel)calculateCPUUsageLevelWithCpuValue:(CGFloat)cpuValue {
    [self.cpuUsageArray addObject:@(cpuValue)];
    /// cpu level calculate
    return CPUUsageLevelLow;
}

/// 计算电量使用级别
- (BatteryUsageLevel)calculateBatteryUsageLevelWithBatteryValue:(CGFloat)batteryValue {
    [self.batteryUsageArray addObject:@(batteryValue)];
    /// battery level calculate
    return BatteryUsageLevelLow;
}

6.1 单个性能参数level计算

CPU:传入数值>0.8,也就是CPU使用率超过80%CPUUsageLevel等于levelMiddle,如果CPU使用率超过100%,则发生CPU超频,CPUUsageLevel等于levelHigh

内存:因为在iOS系统中,App最多可以使用设备总内存的50%,内存使用率超过40%MemoryUsageLevel等于levelMiddle,如果内存使用率超过45%MemoryUsageLevel等于levelHigh

电量:传入数值<6%,则表示低电量,BatteryUsageLevel等于levelMiddle,传入数值<1%,则表示到达临界值,BatteryUsageLevel等于levelHigh

6.2 performanceLevel计算

得到上述三个性能参数的level后,manager会调用decisioncalculatePerformanceLevel方法,通过方法返回值获得performanceLevel,其类型为MultiplePerformanceLevel。计算performanceLevel时,根据先后顺序会有如下条件,条件之间彼此互斥。

1、判断batteryLevel是否等于levelHigh,如果是的话表示电量接近临界值,则直接将performanceLevel设置为veryLow

2、cpuLevel等于overclockmemoryLevel等于high,则表示CPU处于超频状态,并且内存占用也处于非常高的状态,此时很容易被系统强杀造成OOM,直接将performanceLevel设置为veryLow

3、batteryLevelcpuLevelmemoryLevel,任意两者构成middlehigh,则将performanceLevel设置为low

6.3 speedLevel计算

Manager调用decisioncalculateNetworkLevel方法,获取网络变化指标。在计算speedLevel时,传入的网速小于200kb/s,则表示网速较低,将speedLevel设置为low,传入的网速小于50kb/s,则表示网速非常慢,将speedLevel设置为veryLow

6.3.1 性能计算窗口

在获取性能参数时,不能以某一个时间点的性能数据作为计算依据,而是以一个时间窗口的多条性能数据作为计算依据,这样更能反映这个时间段的综合性能。

性能计算窗口是基于handler的回调,收集从当前次到前四次,这连续五次的数据,综合进行计算。例如NetworkSpeedLevel的计算,如果超过三次网速都小于50kb/s,则NetworkSpeedLevel等于veryLow,如果超过三次网速都小于200kb/s,则NetworkSpeedLevel等于low

从实现的角度,性能计算窗口时通过NSMutableArray实现的,通过FIFO策略进行淘汰,始终保留相邻的五条数据。

07

DynamicLevelMonitor

Monitor的作用是提供获取系统性能信息的方法,在handler中调用的三个monitor的方法,内部实现如下。

/// 当前app内存使用量,返回单位百分比
- (NSInteger)useMemoryForApp {
    task_vm_info_data_t vmInfo;
    mach_msg_type_number_t count = TASK_VM_INFO_COUNT;
    kern_return_t kernelReturn = task_info(mach_task_self(), TASK_VM_INFO, (task_info_t) &vmInfo, &count);
    if (kernelReturn == KERN_SUCCESS) {
        int64_t memoryUsageInByte = (int64_t) vmInfo.phys_footprint;
        int64_t totalMemory = [[NSProcessInfo processInfo] physicalMemory];
        return memoryUsageInByte / totalMemory;
    } else {
        return -1;
    }
}

/// 当前app的CPU使用率
- (CGFloat)cpuUsageForApp {
    kern_return_t           kr;
    thread_array_t          thread_list;
    mach_msg_type_number_t  thread_count;
    thread_info_data_t      thinfo;
    mach_msg_type_number_t  thread_info_count;
    thread_basic_info_t     basic_info_th;
    
    kr = task_threads(mach_task_self(), &thread_list, &thread_count);
    if (kr != KERN_SUCCESS)
        return -1;
    
    float total_cpu_usage = 0;
    for (int i = 0; i < thread_count; i++) {
        thread_info_count = THREAD_INFO_MAX;
        kr = thread_info(thread_list[i], THREAD_BASIC_INFO, (thread_info_t)thinfo, &thread_info_count);
        if (kr != KERN_SUCCESS) {
            return -1;
        }
        
        basic_info_th = (thread_basic_info_t)thinfo;
        if (!(basic_info_th->flags & TH_FLAGS_IDLE)) {
            total_cpu_usage += basic_info_th->cpu_usage / (float)TH_USAGE_SCALE;
        }
    }
    
    kr = vm_deallocate(mach_task_self(), (vm_offset_t)thread_list, thread_count * sizeof(thread_t));
    assert(kr == KERN_SUCCESS);
    return total_cpu_usage;
}

UseMemoryForApp方法实现,通过系统task_info函数获取到当前App已使用的内存,通过NSProcessInfophysicalMemory方法获得设备的物理内存,二者的单位都是bytes,通过计算task_infophysicalMemory的百分比,得到App已使用的内存的百分比。

CpuUsageForApp方法实现,通过系统task_threads函数获得所有线程的信息thread_listthread_list是一个数组,遍历thread_list得到thread_info_t单个线程的信息,累加thread_info_tcpu_usage属性(cpu_usage属性表示当前线程使用CPU的百分比),得到总的CPU使用占比。

BatteryUsageForApp方法实现,设置系统UIDevicebatteryMonitoringEnabledtrue,开启电量监听。并通过通知接收电量变化的回调,回调的单位是0~1,再乘以100返回给manager

08

业务方

业务方收到PerformanceLevelChanged的消息后,可以基于performanceLevel的综合性能进行判断,如果是veryLow,可以暂停流内秒播处理,也就是在视频流中,滑动到下一条视频不会自动播放。

也可以基于单个性能level进行判断,例如batteryLevel指标为middlelow,也就是电量低于6%时,可以提示用户先不进行视频文件缓存等非常消耗性能的操作,以避免因为消耗性能的操作,导致手机自动关机。

业务方收到NetworkSpeedLevelChanged的消息后,可以根据通知传过来的speedLevel参数,lowveryLow可以有不同的处理。例如可以降低向服务端获取图片的尺寸,low可以将图片尺寸压缩80%,如果是veryLow可以将图片尺寸压缩60%,可以明显提升弱网下,向服务器获取图片的速度。压缩比率在请求图片URL时,在URL中拼接发送给服务端,服务端会返回对应压缩比率的图片。

28bf0080527b36e59706b3a23175ed60.jpeg

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

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

相关文章

centos7安装mysql5.7笔记

1 配置yum仓库 1.1更新密钥 #更新密钥 rpm --import https://repo.mysql.com/RPM-GPG-KEY-mysql-2022 1.2 下载使用wget命令下载MySQL的repo文件 #下载使用wget命令下载MySQL的repo文件 wget https://dev.mysql.com/get/mysql57-community-release-el7-11.noarch.rpm 2 使用…

智能生活新体验:小米香薰加湿器技术解码

在现代家居生活中&#xff0c;科技与舒适性日益交织&#xff0c;智能家居产品成为提升生活品质的重要工具。小米香薰加湿器作为一款集科技与生活美学于一体的产品&#xff0c;其独特的设计和多功能性受到了广泛欢迎。今天&#xff0c;我们就来详细拆解这款融合了科技与香薰元素…

大型语言模型的谎言危机:我们怎样揭穿科技巨头的误导游戏?|TodayAI

昨天&#xff0c;Meta推出了号称至今最强大的大型语言模型LLaMA 3&#xff0c;引发了人们对该技术的极大热情。然而&#xff0c;这股热情伴随着一些问题。包括Meta、谷歌和推特在内的几家公司被指散布了大量误导性信息&#xff0c;这些信息对用户和投资者关于大型语言模型的能力…

20240327-2-贝叶斯面试题NaïveBayes

贝叶斯面试题 1.简述朴素贝叶斯算法原理和工作流程 工作原理&#xff1a; 假设现在有样本 x ( x 1 , x 2 , x 3 , … x n ) x(x_1, x_2, x_3, \dots x_n) x(x1​,x2​,x3​,…xn​)待分类项假设样本有 m m m个特征 ( a 1 , a 2 , a 3 , … a m ) (a_1,a_2,a_3,\dots a_m) (a…

大型网站系统架构演化实例_5.使用反向代理和CDN加速网站响应

1.使用反向代理和CDN加速网站响应 随着网站业务不断发展&#xff0c;用户规模越来越大&#xff0c;由于区域的差别使得网络环境异常复杂&#xff0c;不同地区的用户访问网站时&#xff0c;速度差别也极大。有研究表明&#xff0c;网站访问延迟和用户流失率正相关&#xff0c;网…

【Linux】认识文件(一):文件标识符

【Linux】认识文件&#xff08;一&#xff09;&#xff1a;文件标识符 一.什么是文件&#xff1f;1.文件的本质2.文件的分类 二.访问文件操作1.C语言中的访问文件接口i.fopenii.fcloseiii.fwrite 2.系统访问文件接口i.openii.closeiii.write 三.文件管理1.对所有打开文件的管理…

ubuntu22.04搭建dns内网

近期&#xff0c;需要在无网络的ubuntu环境下搭建内部可用的dns内网&#xff0c;总共花费3个工作日晚上&#xff0c;总算成功搭建&#xff0c;做个记录&#xff0c;记录踩坑记录&#xff0c;同时方便以后翻阅。 安装软件包&#xff1a; 有网络环境下&#xff0c;比较简单&…

PostgreSQL中的索引类型有哪些,以及何时应选择不同类型的索引?

文章目录 索引 解决方案和示例代码 PostgreSQL提供了多种索引类型&#xff0c;每种类型都有其特定的应用场景和优势。选择合适的索引类型可以显著提高查询性能&#xff0c;减少数据库负载。 索引 以下是PostgreSQL中常见的索引类型及其适用场景&#xff1a; 1. B-tree 索引 …

【Linux 开发第一篇】如何在安装中完成自定义配置分区

安装配置自定义配置分区 在安装Centos的过程中&#xff0c;我们可以在安装位置部分手动配置分区 选择我要配置分区&#xff0c;点击完成&#xff1a; 我们自动分区分为三个分区&#xff1a;boot分区&#xff08;引导分区&#xff09;&#xff0c;swap&#xff08;交换分区&…

互联网技术知识点总览——操作系统知识点框架图

简介 本文对操作系统的知识点整体框架进行梳理和分享如下&#xff1a;

KaiwuDB CTO 魏可伟:AIoT,用行业定义数据库

4月12日&#xff0c;由中国 DBA 联盟&#xff08;ACDU&#xff09;与墨天轮社区联合主办的第十三届数据技术嘉年华&#xff08;DTC 2024&#xff09;于北京盛大召开。KaiwuDB CTO 魏可伟受邀发表《智创当下&#xff0c;KaiwuDB 从多模到 AI 的探索实践》主题演讲&#xff0c;向…

Go之map详解

map的结构 map实现的两个关键数据结构 hmap 定义了map的结构bmap 定义了hmap.buckets中每个bucket的结构 // A header for a Go map. type hmap struct {count int // 元素的个数flags uint8 // 状态标记&#xff0c;标记map当前状态&#xff0c;是否正在写入B …

VASP结合vaspkit+ShengBTE计算热电优值(二)

前文链接&#xff1a;VASP结合vaspkitShengBTE计算热电优值&#xff08;一&#xff09; 1、将前述计算得到的二阶力常数矩阵&#xff0c;三阶力常数矩阵文件分别命名为FORCE_CONSTANTS_2RD&#xff0c;FORCE_CONSTANTS_3RD。放于同一目录中。 编写CONTROL文件&#xff0c;其中…

借助 NVivo 彻底改变业务创新

在收集定性数据时&#xff0c;通常很难确定信息的情感底蕴。尤其是在金融行业&#xff0c;当涉及到经济金融状况和股票走势等问题时&#xff0c;通过文章、社交媒体和其他消费者平台了解市场的真实整体感受至关重要。这就是对数据应用情绪分析可以提供帮助的地方。 在德勤 针对…

追溯历史:SIEM 中的生成式人工智能革命

作者&#xff1a;来自 Elastic Mike Nichols, Mike Paquette 网络安全领域仿佛是现实世界的一个映射&#xff0c;安全运营中心&#xff08;security operation center - SOC&#xff09;就像是你的数字警察局。网络安全分析师就像是警察&#xff0c;他们的工作是阻止网络犯罪分…

【webrtc】m114自己实现的PrioritizedPacketQueue及优先级处理

G:\CDN\WEBRTC-DEV\libwebrtc_build\src\modules\pacing\prioritized_packet_queue.h跟m98不同 :webrtc】m98 RoundRobinPacketQueue的优先级处理,m114直接使用taskqueue顺序处理了。甚至自己实现了优先级队列感觉简化了实现,更为清晰 易读,但是去掉了码率低就优先的逻辑。1…

Linux程序的地址空间,进程终止

个人主页&#xff1a;点我进入主页 专栏分类&#xff1a;C语言初阶 C语言进阶 数据结构初阶 Linux C初阶 算法 欢迎大家点赞&#xff0c;评论&#xff0c;收藏。 一起努力&#xff0c;一起奔赴大厂 一.程序的地址空间 1.1程序的地址空间的引入 我们知道frok可以创建…

R语言入门:“Hellinger“转化和“normalize“转化(弦转化)的公式表示与R代码实现

1、写在前面 vegan包中的decostand()函数为群落生态学研究提供了一些流行的(和有效的)标准化方法。有关decostand()函数标准化的一些标准化方法可以看我的另一篇笔记&#xff1a;R语言入门&#xff1a;vegan包使用decostand()函数标准化方法 由于在网络上没有找到关于这两个转…

AI 语音机器人系统怎么搭建

搭建AI语音机器人系统通常包括以下几个关键步骤&#xff1a; 确定需求和技术选型&#xff1a;首先要明确AI语音机器人需要实现的功能&#xff0c;选择合适的技术框架和工具&#xff0c;如自然语言处理工具、语音识别工具等。 搜集和准备数据&#xff1a;收集和整理与业务相关…

2.Python实战小项目—用Python批量压缩图片

2.Python实战小项目—用Python批量压缩图片 一摘要二个人简介三原理四流程五实战演示 一摘要 在Python中&#xff0c;批量压缩图片是一项相对直接且实用的任务&#xff0c;尤其适合需要处理大量图像数据的场合。Pillow库提供了一种简便的方式来达成这个目标&#xff0c;其强大的…