-
# 【云驻共创】鲲鹏DevKit“0”门槛快速调优的秘密武器 鲲鹏DevKit调优助手通过系统化组织和分析性能指标、热点函数、系统配置等信息,形成系统资源消耗链条,引导用户根据优化路径分析性能瓶颈,并针对每条优化路径给出优化建议和操作指导,以此实现快速调优。解决客户软件运行遇到性能问题时凭人工经验定位困难,调优能力弱的痛点。 # 1、**鲲鹏DevKit介绍** 鲲鹏开发套件Kunpeng DevKit提供全栈开发工具,集代码迁移、编译调试、性能调优、异常诊断等工具和功能于一体。将开发者的工作各个环节一一串联,提供了一个方便、快捷和专业的工具包,通过鲲鹏DevKit可以帮助用户高效开发,一展宏图。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20224/20/1650421661908334803.png) # 2、**性能分析工具简介** 鲲鹏性能分析工具由四个子工具组成,分别为:系统性能分析、Java性能分析、系统诊断和调优助手。 系统性能分析是针对基于鲲鹏的服务器的性能分析工具,能收集服务器的处理器硬件、操作系统、进程/线程、函数等各层次的性能数据,分析系统性能指标,定位到系统瓶颈点及热点函数,并给出优化建议。该工具可以辅助用户快速定位和处理软件性能问题。 Java性能分析是针对基于鲲鹏的服务器上运行的Java程序的性能分析和优化工具,能图形化显示Java程序的堆、线程、锁、垃圾回收等信息,收集热点函数、定位程序瓶颈点,帮助用户采取针对性优化。 系统诊断是针对基于鲲鹏的服务器的性能分析工具,提供内存泄漏诊断(包括内存未释放和异常释放)、内存越界诊断、内存消耗信息分析展示、OOM诊断能力、网络丢包等,帮助用户识别出源代码中内存使用的问题点,提升程序的可靠性,工具还支持压测系统,如:网络IO诊断,评估系统最大性能。 调优助手是针对基于鲲鹏的服务器的调优工具,能系统化组织性能指标,引导用户分析性能瓶颈,实现快速调优。 其中,调优助手通过系统化组织和分析性能指标、热点函数、系统配置等信息,形成系统资源消耗链条,引导用户根据优化路径分析性能瓶颈,并针对每条优化路径给出优化建议和操作指导,以此实现快速调优。解决客户软件运行遇到性能问题时凭人工经验定位困难,调优能力弱的痛点。 ![image.png](https://bbs-img.huaweicloud.com/data/forums/attachment/forum/20224/20/1650421675155605669.png) # 3、 性能分析工具应用场景 客户软件在基于鲲鹏的服务器上运行遇到性能问题时,可用系统性能分析来快速分析和定位。 系统性能分析工具将采集系统如下数据: - 系统软硬件配置和运行信息,例如:CPU类型、内存部署槽位、Kernel版本、内核参数、文件系统、系统运行日志参数等。 - 系统的CPU、内存、存储IO、磁盘IO等性能指标。 - 处理器PMU、SPE的性能数据。 - 处理器访问Cache/内存的次数、带宽、吞吐率等。 - 系统内核进行CPU资源调度、IO操作等数据。 - 进程/线程的CPU、内存、存储IO、上下文切换、系统调用等数据;进程命令行信息,包括:进程名、进程参数。 - 系统的热点函数及其调用栈;热点函数归属的程序/动态库(包含绝对路径);热点函数的汇编指令和热点指令;热点函数所对应的源代码(需要用户自行提供)。 客户Java应用软件在基于鲲鹏的服务器上运行遇到性能问题时,可用Java性能分析来快速分析和定位。 Java性能分析工具将采集如下数据: - Java进程运行环境信息,如:PID、JVM版本、JAVA版本、Main class、进程启动参数等。 - Java进程的CPU活动、内存占用、类加载信息、线程运行状态信息、系统的存储容量等。 - 可触发采集进程的堆转储信息,并获取堆转储中的类,实例,对象引用链等信息。 - Java进程的文件IO、SocketIO操作、数据库操作、HTTP请求、SpringBoot运行信息等。 - 对Java进程的方法、线程、内存、老年代对象、调用栈进行采样生成JFR采样文件。 客户软件在基于鲲鹏的服务器上运行遇到性能问题时,可用系统诊断来快速分析和定位。 系统诊断工具将采集系统如下数据: - 内存泄漏次数 - 内存泄漏大小 - 内存异常释放次数 - self内存泄漏 - 调用栈信息 - 系统的物理内存和虚拟内存大小 - 进程的内存MAP信息 - 应用的申请内存大小、申请次数、申请字节数、释放次数、释放字节数、泄漏次数以及泄漏字节数 - 分配器的分配内存、空闲内存、使用内存、Arena数、mmap区域数量以及mmap区域大小。 - 系统软硬件配置和运行信息,例如:CPU类型、内存部署槽位、Kernel版本、内核参数、文件系统、系统运行日志参数等。 - 系统的CPU、内存、存储IO、磁盘IO等性能指标。 - 处理器PMU、SPE的性能数据。 - 处理器访问Cache/内存的次数、带宽、吞吐率等。 - 系统内核进行CPU资源调度、IO操作等数据。 - 进程/线程的CPU、内存、存储IO、上下文切换、系统调用等数据;进程命令行信息,包括:进程名、进程参数。 - 系统的热点函数及其调用栈;热点函数归属的程序/动态库(包含绝对路径);热点函数的汇编指令和热点指令;热点函数所对应的源代码(需要用户自行提供)。 # 4、 实现原理 调优助手采集的数据覆盖OS、应用、硬件等系统各层的配置和性能指标,并根据硬件资源的消耗,来关联消耗这些硬件资源的软件信息,再从这些软件信息,来查看软件对其它的硬件资源消耗,从而推断出性能瓶颈。将数据从应用消耗、物理消耗以及硬件关联在一起。 - **Analysis Server**:实现性能数据分析及分析结果呈现。 | 表1 Analysis Server模块介绍 | | | --------------------------- | ------------------------------------------------------------ | | **模块名** | **功能** | | Web Browser | Web浏览器,用于操作交互和数据呈现。 | | Web Server | Web服务器,接收Web浏览器的请求,并触发Data Analysis Framework进行具体的业务处理。 | | Data Analysis Framework | 数据分析框架,主要作用是:o 通知Data Collection Framework进行数据采集,并接收采集的数据文件。o 调用相应的Data Analysis Plugin对数据文件进行入库和分析,并保存分析结果。o 为Web 服务器提供分析结果查询通道。 | | Data Analysis Plugin | 数据分析插件,不同性能分析功能有对应的分析插件,主要作用是:o 对数据文件进行预处理,并导入数据库中。o 分析原始数据,得出更适合展示的数据格式及数据间的关联关系,并结合以往项目调优经验值,给出优化建议。o 提供性能分析结果查询接口。 | - **Agent**:实现性能数据采集。 | 表2 Agent模块介绍 | | | ------------------------- | ------------------------------------------------------------ | | **模块名** | **功能** | | Data Collection Framework | 数据采集框架,主要作用是:o 接收Data Analysis Framework的采集通知,调用相应的Data Collection Plugin进行数据采集。o 将采集的数据文件推送给Data Analysis Framework。 | | Data Collection Plugin | 数据采集插件,不同性能分析功能有对应的采集插件,主要作用是完成具体的性能数据采集,并保存到文件。 | 说明: 鲲鹏性能分析工具只采集系统运行过程中的性能数据,不采集用户数据,不会造成客户信息泄露。
-
1、关于鲲鹏 1.1、鲲鹏介绍 鲲鹏计算产业是基于鲲鹏处理器的基础软硬件设施、行业应用及服务,涵盖从底层硬件、基础软件到上层行业应用的全产业链条。华为作为鲲鹏计算产业的成员,聚焦计算架构创新、处理器和开源基础软件的研发,以及华为云服务,致力于推动鲲鹏生态发展。通过战略性、长周期的研发投入,吸纳全球计算产业的优秀人才和先进技术,持续推进全栈计算技术的创新发展,加快构筑面向多样性计算的全球开源体系与产业标准。基于“硬件开放、软件开源、使能伙伴、发展人才”的策略推动鲲鹏计算产业发展。 1.2、鲲鹏解决方案 鲲鹏全栈解决方案,主要应用在金融、互联网、运营商、政府、电力、交通等行业。其中应用使能套件BoostKit可应用于大数据、分布式存储、数据库、虚拟化ARM原生等方面。基础软件可应用于openGauss企业级开源数据库、openEuler开源操作系统。开发套件DevKit包含鲲鹏代码迁移工具、鲲鹏编译器、鲲鹏性能分析工具、动态二进制翻译工具。 2、鲲鹏开发套件DevKit 2.1、DevKit介绍 鲲鹏开发套件DevKit提供涵盖代码开发、编译调试、云测服务、性能分析及系统诊断等各环节的开发使能工具,方便开发者快速开发出鲲鹏亲和的高性能软件,帮助开发者加速应用迁移和算力升级。同时面向全研发作业流程,提升应用迁移和调优效率,加速原生开发。 鲲鹏开发套件DevKit以开发者为中心,并提升全流程开发效率。 开发套件DevKit包含鲲鹏代码迁移工具、鲲鹏编译器、性能分析工具、动态二进制翻译工具等。 2.2、鲲鹏代码迁移工具 2.2.1、工具简介 鲲鹏代码迁移工具是一款可以简化客户应用迁移到基于鲲鹏916/920的服务器的过程的工具。工具仅支持x86 Linux到Kunpeng Linux的扫描与分析,不支持Windows软件代码的扫描、分析与迁移。 当客户有x86平台上源代码的软件要迁移到基于鲲鹏916/920的服务器上时,既可以使用该工具分析可迁移性和迁移投入,也可以使用该工具自动分析出需修改的代码内容,并指导用户如何修改。 鲲鹏代码迁移工具既解决了客户软件迁移评估分析过程中人工分析投入大、准确率低、整体效率低下的痛点,通过该工具能够自动分析并输出指导报告;也解决了用户代码兼容性人工排查困难、迁移经验欠缺、反复依赖编译调错定位等痛点。 2.2.2、应用场景 软件迁移评估:自动扫描并分析软件包(非源码包)、已安装的软件,提供可迁移性评估报告。 源码迁移:当用户有软件要迁移到基于鲲鹏916/920的服务器上时,可先用该工具分析源码并得到迁移修改建议。 软件包重构:帮助用户重构适用于鲲鹏平台的软件安装包。 专项软件迁移:使用华为提供的软件迁移模板修改、编译并产生指定软件版本的安装包,该软件包适用于鲲鹏平台。 增强功能:支持x86和鲲鹏平台GCC 4.8.5~GCC 9.3.0版本32位应用向64位应用迁移的64位运行模式检查,结构体字节对齐检查、缓存行对齐检查和鲲鹏平台上的内存一致性检查。 2.2.3、部署方式 单机部署,即将鲲鹏代码迁移工具部署在用户的开发、测试的x86服务器或者基于鲲鹏916/920的服务器。 2.3、鲲鹏性能分析工具 2.3.1、工具简介 鲲鹏性能分析工具由四个子工具组成,分别为:系统性能分析、Java性能分析、系统诊断和调优助手。 系统性能分析是针对基于鲲鹏的服务器的性能分析工具,能收集服务器的处理器硬件、操作系统、进程/线程、函数等各层次的性能数据,分析系统性能指标,定位到系统瓶颈点及热点函数,并给出优化建议。该工具可以辅助用户快速定位和处理软件性能问题。 Java性能分析是针对基于鲲鹏的服务器上运行的Java程序的性能分析和优化工具,能图形化显示Java程序的堆、线程、锁、垃圾回收等信息,收集热点函数、定位程序瓶颈点,帮助用户采取针对性优化。 系统诊断是针对基于鲲鹏的服务器的性能分析工具,提供内存泄漏诊断(包括内存未释放和异常释放)、内存越界诊断、内存消耗信息分析展示、OOM诊断能力、网络丢包等,帮助用户识别出源代码中内存使用的问题点,提升程序的可靠性,工具还支持压测系统,如:网络IO诊断,评估系统最大性能。 调优助手是针对基于鲲鹏的服务器的调优工具,能系统化组织性能指标,引导用户分析性能瓶颈,实现快速调优。 2.3.2、应用场景 客户软件在基于鲲鹏的服务器上运行遇到性能问题时,可用系统性能分析来快速分析和定位。 系统性能分析工具将采集系统如下数据: 系统软硬件配置和运行信息,例如:CPU类型、内存部署槽位、Kernel版本、内核参数、文件系统、系统运行日志参数等。 系统的CPU、内存、存储IO、磁盘IO等性能指标。 处理器PMU、SPE的性能数据。 处理器访问Cache/内存的次数、带宽、吞吐率等。 系统内核进行CPU资源调度、IO操作等数据。 进程/线程的CPU、内存、存储IO、上下文切换、系统调用等数据;进程命令行信息,包括:进程名、进程参数。 系统的热点函数及其调用栈;热点函数归属的程序/动态库(包含绝对路径);热点函数的汇编指令和热点指令;热点函数所对应的源代码(需要用户自行提供)。 2.3.3、部署方式 当前版本支持灵活部署,即将系统性能分析所有组件部署在一台服务器上、不同服务器上及混合部署,完成性能数据采集和分析。 2.4、鲲鹏开发套件插件工具(VSCode) 2.4.1、工具简介 鲲鹏开发套件插件工具是基于Visual Studio Code提供给开发者面向鲲鹏平台进行应用软件开发、迁移、编译调试、性能调优等一系列端到端工具,即插即用。一体化呈现代码迁移插件、鲲鹏开发框架插件、编译插件及性能分析插件的完整开发套件。 鲲鹏开发套件插件工具是一个工具集,由多个插件组成,支持IDE前端界面,支持一键式安装后端,代码编辑体验增强,自动检测安装鲲鹏编译器,编译调试,用例可视化,编码辅助,工程分析扫描。用户可以通过安装Kunpeng DevKit插件直接将四个插件都安装好,也可以单独选择个别插件安装使用。 2.4.2、代码迁移插件 鲲鹏代码迁移插件作为客户端调用服务端的功能,完成扫描迁移任务,可以对待迁移软件进行快速扫描分析,并提供专业的代码迁移指导,极大简化客户应用迁移到鲲鹏平台的过程。当客户有软件需要迁移到鲲鹏平台上时,可先用该工具分析可迁移性和迁移投入,以解决客户软件迁移评估中分析投入大、准确率低、整体效率低下的痛点。 代码迁移工具支持五个功能特性: 软件迁移评估:自动扫描并分析软件包(非源码包)、已安装的软件,提供可迁移性评估报告。 源码迁移:能够自动检查并分析出用户源码、C/C++/ASM/Fortran/解释型语言/汇编软件构建工程文件、C/C++/ASM/Fortran/解释型语言/汇编软件构建工程文件使用的链接库、x86汇编代码中需要修改的内容,并给出修改指导,以解决用户代码兼容性排查困难、迁移经验欠缺、反复依赖编译调错定位等痛点。 软件包重构:通过分析x86平台软件包(RPM格式、DEB格式)的软件构成关系及硬件依赖性,重构适用于鲲鹏平台的软件包。 专项软件迁移:基于鲲鹏解决方案的软件迁移模板,进行自动化迁移修改、编译、构建软件包,帮助用户快速迁移软件。 2.4.3、性能分析插件 鲲鹏开发套件是Visual Studio Code的一款扩展工具,通常将此类工具称作集成开发环境(IDE)插件。 鲲鹏性能分析插件是其中一个子工具,作为客户端调用服务端的功能。 鲲鹏性能分析工具由四个子工具组成,分别为:系统性能分析、Java性能分析、系统诊断和调优助手。 系统性能分析是针对基于鲲鹏的服务器的性能分析工具,能收集服务器的处理器硬件、操作系统、进程/线程、函数等各层次的性能数据,分析系统性能指标,定位到系统瓶颈点及热点函数,并给出优化建议。该工具可以辅助用户快速定位和处理软件性能问题。 Java性能分析是针对基于鲲鹏的服务器上运行的Java程序的性能分析和优化工具,能图形化显示Java程序的堆、线程、锁、垃圾回收等信息,收集热点函数、定位程序瓶颈点,帮助用户采取针对性优化。 系统诊断是针对基于鲲鹏的服务器的性能分析工具,提供内存泄漏诊断(包括内存未释放和异常释放)、内存越界诊断、内存消耗信息分析展示、OOM诊断能力,帮助用户识别出源代码中内存使用的问题点,提升程序的可靠性;压测网络,获得网络最大能力,为网络IO性能优化提供基础参考数据;诊断网络,定位网络疑难问题,解决因网络配置和异常而导致的网络IO性能问题;压测存储IO,获得存储设备最大能力,包括:吞吐量、IOPS、时延等,并以此评估存储能力,为存储IO性能优化提供基础参考数据。 2.5、二进制动态翻译工具 2.5.1、相关概念 ExaGear是一款二进制指令动态翻译软件,运行在ARM64服务器上,通过将x86的指令在运行时翻译为ARM64指令并执行,使得绝大部分Linux on x86应用无需重新编译就可运行在ARM64服务器上,实现低成本、快速迁移Linux on x86应用到ARM64服务器。 2.5.2、关键特性 支持多种部署方式:支持在物理机、虚拟机、容器等平台上部署; 部署简单:一键式快速安装,x86应用部署和运行与迁移前保持一致; 支持多版本Linux OS:目前支持CentOS 7、CentOS 8、Ubuntu18、Ubuntu20、OpenEuler 20.03,并且根据用户需求,未来可定制支持更多Linux OS发行; 低损耗: 大多数场景的应用,翻译损耗在20%以内。 3、结束语 对鲲鹏开发套件有兴趣的同学可参考如下链接进行进一步学习。 相关链接:https://support.huaweicloud.com/kunpengdevps/kunpengdevps.html
-
## Linux关机,重启 ``` # 关机 shutdown -h now # 重启 shutdown -r now ``` ## 查看系统,CPU信息 ``` # 查看系统内核信息 uname -a # 查看系统内核版本 cat /proc/version # 查看当前用户环境变量 env cat /proc/cpuinfo # 查看有几个逻辑cpu, 包括cpu型号 cat /proc/cpuinfo | grep name | cut -f2 -d: | uniq -c # 查看有几颗cpu,每颗分别是几核 cat /proc/cpuinfo | grep physical | uniq -c # 查看当前CPU运行在32bit还是64bit模式下, 如果是运行在32bit下也不代表CPU不支持64bit getconf LONG_BIT # 结果大于0, 说明支持64bit计算. lm指long mode, 支持lm则是64bit cat /proc/cpuinfo | grep flags | grep ' lm ' | wc -l ``` ## 建立软连接 ``` ln -s /usr/local/jdk1.8/ jdk ``` ## rpm相关 ``` # 查看是否通过rpm安装了该软件 rpm -qa | grep 软件名 ``` ## sshkey ``` # 创建sshkey ssh-keygen -t rsa -C your_email@example.com #id_rsa.pub 的内容拷贝到要控制的服务器的 home/username/.ssh/authorized_keys 中,如果没有则新建(.ssh权限为700, authorized_keys权限为600) ``` ## 命令重命名 ``` # 在各个用户的.bash_profile中添加重命名配置 alias ll='ls -alF' ``` ## 同步服务器时间 ``` sudo ntpdate -u ntp.api.bz ``` ## 后台运行命令 ``` # 后台运行,并且有nohup.out输出 nohup xxx & # 后台运行, 不输出任何日志 nohup xxx > /dev/null & # 后台运行, 并将错误信息做标准输出到日志中 nohup xxx >out.log 2>&1 & ``` ## 强制活动用户退出 ``` # 命令来完成强制活动用户退出.其中TTY表示终端名称 pkill -kill -t [TTY] ``` ## 查看命令路径 ``` which 命令> ``` ## 查看进程所有打开最大fd数 ``` ulimit -n ``` ## 配置dns ``` vim /etc/resolv.conf ``` ## nslookup,查看域名路由表 ``` nslookup google.com ``` ## last, 最近登录信息列表 ``` # 最近登录的5个账号 last -n 5 ``` ## 设置固定ip ``` ifconfig em1 192.168.5.177 netmask 255.255.255.0 ``` ## 查看进程内加载的环境变量 ``` # 也可以去 cd /proc 目录下, 查看进程内存中加载的东西 ps eww -p XXXXX(进程号) ``` ## 查看进程树找到服务器进程 ``` ps auwxf ``` ## 查看进程启动路径 ``` cd /proc/xxx(进程号) ls -all # cwd对应的是启动路径 ``` ## 添加用户, 配置sudo权限 ``` # 新增用户 useradd 用户名 passwd 用户名 #增加sudo权限 vim /etc/sudoers # 修改文件里面的 # root ALL=(ALL) ALL # 用户名 ALL=(ALL) ALL ``` ## 强制关闭进程名包含xxx的所有进程 ``` ps aux|grep xxx | grep -v grep | awk '{print $2}' | xargs kill -9 ``` ------ # 磁盘,文件,目录相关操作 ## vim操作 ``` #normal模式下 g表示全局, x表示查找的内容, y表示替换后的内容 :%s/x/y/g #normal模式下 0 # 光标移到行首(数字0) $ # 光标移至行尾 shift + g # 跳到文件最后 gg # 跳到文件头 # 显示行号 :set nu # 去除行号 :set nonu # 检索 /xxx(检索内容) # 从头检索, 按n查找下一个 ?xxx(检索内容) # 从尾部检索 ``` ## 打开只读文件,修改后需要保存时(不用切换用户即可保存的方式) ``` # 在normal模式下 :w !sudo tee % ``` ## 查看磁盘, 文件目录基本信息 ``` # 查看磁盘挂载情况 mount # 查看磁盘分区信息 df # 查看目录及子目录大小 du -H -h # 查看当前目录下各个文件, 文件夹占了多少空间, 不会递归 du -sh * ``` ## wc命令 ``` # 查看文件里有多少行 wc -l filename # 看文件里有多少个word wc -w filename # 文件里最长的那一行是多少个字 wc -L filename # 统计字节数 wc -c ``` ## 常用压缩, 解压缩命令 ### 压缩命令 ``` tar czvf xxx.tar 压缩目录 zip -r xxx.zip 压缩目录 ``` ### 解压缩命令 ``` tar zxvf xxx.tar # 解压到指定文件夹 tar zxvf xxx.tar -C /xxx/yyy/ unzip xxx.zip ``` ## 变更文件所属用户, 用户组 ``` chown eagleye.eagleye xxx.log ``` ## cp, scp, mkdir ``` #复制 cp xxx.log # 复制并强制覆盖同名文件 cp -f xxx.log # 复制文件夹 cp -r xxx(源文件夹) yyy(目标文件夹) # 远程复制 scp -P ssh端口 username@10.10.10.101:/home/username/xxx /home/xxx # 级联创建目录 mkdir -p /xxx/yyy/zzz # 批量创建文件夹, 会在test,main下都创建java, resources文件夹 mkdir -p src/{test,main}/{java,resources} ``` ## 比较两个文件 ``` diff -u 1.txt 2.txt ``` ## 日志输出的字节数,可以用作性能测试 ``` # 如果做性能测试, 可以每执行一次, 往日志里面输出 “.” , 这样日志中的字节数就是实际的性能测试运行的次数, 还可以看见实时速率. tail -f xxx.log | pv -bt ``` ## 查看, 去除特殊字符 ``` # 查看特殊字符 cat -v xxx.sh # 去除特殊字符 sed -i 's/^M//g’ env.sh 去除文件的特殊字符, 比如^M: 需要这样输入: ctrl+v+enter ``` ## 处理因系统原因引起的文件中特殊字符的问题 ``` # 可以转换为该系统下的文件格式 cat file.sh > file.sh_bak # 先将file.sh中文件内容复制下来然后运行, 然后粘贴内容, 最后ctrl + d 保存退出 cat > file1.sh # 在vim中通过如下设置文件编码和文件格式 :set fileencodings=utf-8 ,然后 w (存盘)一下即可转化为 utf8 格式, :set fileformat=unix # 在mac下使用dos2unix进行文件格式化 find . -name "*.sh" | xargs dos2unix ``` ## tee, 重定向的同时输出到屏幕 ``` awk ‘{print $0}’ xxx.log | tee test.log ``` ------ # 检索相关 ## grep ``` # 反向匹配, 查找不包含xxx的内容 grep -v xxx # 排除所有空行 grep -v '^$' # 返回结果 2,则说明第二行是空行 grep -n “^$” 111.txt # 查询以abc开头的行 grep -n “^abc” 111.txt # 同时列出该词语出现在文章的第几行 grep 'xxx' -n xxx.log # 计算一下该字串出现的次数 grep 'xxx' -c xxx.log # 比对的时候,不计较大小写的不同 grep 'xxx' -i xxx.log ``` ## awk ``` # 以':' 为分隔符,如果第五域有user则输出该行 awk -F ':' '{if ($5 ~ /user/) print $0}' /etc/passwd # 统计单个文件中某个字符(串)(中文无效)出现的次数 awk -v RS='character' 'END {print --NR}' xxx.txt ``` ## find检索命令 ``` # 在目录下找后缀是.mysql的文件 find /home/eagleye -name '*.mysql' -print # 会从 /usr 目录开始往下找,找最近3天之内存取过的文件。 find /usr -atime 3 –print # 会从 /usr 目录开始往下找,找最近5天之内修改过的文件。 find /usr -ctime 5 –print # 会从 /doc 目录开始往下找,找jacky 的、文件名开头是 j的文件。 find /doc -user jacky -name 'j*' –print # 会从 /doc 目录开始往下找,找寻文件名是 ja 开头或者 ma开头的文件。 find /doc \( -name 'ja*' -o- -name 'ma*' \) –print # 会从 /doc 目录开始往下找,找到凡是文件名结尾为 bak的文件,把它删除掉。-exec 选项是执行的意思,rm 是删除命令,{ } 表示文件名,“\;”是规定的命令结尾。 find /doc -name '*bak' -exec rm {} \; ``` ------ # 网络相关 ## 查看什么进程使用了该端口 ``` lsof -i:port ``` ## 获取本机ip地址 ``` /sbin/ifconfig -a|grep inet|grep -v 127.0.0.1|grep -v inet6|awk '{print $2}'|tr -d "addr:" ``` ## iptables ``` # 查看iptables状态 service iptables status # 要封停一个ip iptables -I INPUT -s ***.***.***.*** -j DROP # 要解封一个IP,使用下面这条命令: iptables -D INPUT -s ***.***.***.*** -j DROP 备注: 参数-I是表示Insert(添加),-D表示Delete(删除)。后面跟的是规则,INPUT表示入站,***.***.***.***表示要封停的IP,DROP表示放弃连接。 #开启9090端口的访问 /sbin/iptables -I INPUT -p tcp --dport 9090 -j ACCEPT # 防火墙开启、关闭、重启 /etc/init.d/iptables status /etc/init.d/iptables start /etc/init.d/iptables stop /etc/init.d/iptables restart ``` ## nc命令, tcp调试利器 ``` #给某一个endpoint发送TCP请求,就将data的内容发送到对端 nc 192.168.0.11 8000 data.txt #nc可以当做服务器,监听某个端口号,把某一次请求的内容存储到received_data里 nc -l 8000 > received_data #上边只监听一次,如果多次可以加上-k参数 nc -lk 8000 ``` ## tcpdump ``` # dump出本机12301端口的tcp包 tcpdump -i em1 tcp port 12301 -s 1500 -w abc.pcap ``` ## 跟踪网络路由路径 ``` # traceroute默认使用udp方式, 如果是-I则改成icmp方式 traceroute -I www.163.com # 从ttl第3跳跟踪 traceroute -M 3 www.163.com # 加上端口跟踪 traceroute -p 8080 192.168.10.11 ``` ## ss ``` # 显示本地打开的所有端口 ss -l # 显示每个进程具体打开的socket ss -pl # 显示所有tcp socket ss -t -a # 显示所有的UDP Socekt ss -u -a # 显示所有已建立的SMTP连接 ss -o state established '( dport = :smtp or sport = :smtp )' # 显示所有已建立的HTTP连接 ss -o state established '( dport = :http or sport = :http )' 找出所有连接X服务器的进程 ss -x src /tmp/.X11-unix/* 列出当前socket统计信息 ss -s 解释:netstat是遍历/proc下面每个PID目录,ss直接读/proc/net下面的统计信息。所以ss执行的时候消耗资源以及消耗的时间都比netstat少很多 ``` ## netstat ``` # 输出每个ip的连接数,以及总的各个状态的连接数 netstat -n | awk '/^tcp/ {n=split($(NF-1),array,":");if(n=2)++S[array[(1)]];else++S[array[(4)]];++s[$NF];++N} END {for(a in S){printf("%-20s %s\n", a, S[a]);++I}printf("%-20s %s\n","TOTAL_IP",I);for(a in s) printf("%-20s %s\n",a, s[a]);printf("%-20s %s\n","TOTAL_LINK",N);}' # 统计所有连接状态, # CLOSED:无连接是活动的或正在进行 # LISTEN:服务器在等待进入呼叫 # SYN_RECV:一个连接请求已经到达,等待确认 # SYN_SENT:应用已经开始,打开一个连接 # ESTABLISHED:正常数据传输状态 # FIN_WAIT1:应用说它已经完成 # FIN_WAIT2:另一边已同意释放 # ITMED_WAIT:等待所有分组死掉 # CLOSING:两边同时尝试关闭 # TIME_WAIT:主动关闭连接一端还没有等到另一端反馈期间的状态 # LAST_ACK:等待所有分组死掉 netstat -n | awk '/^tcp/ {++state[$NF]} END {for(key in state) print key,"\t",state[key]}' # 查找较多time_wait连接 netstat -n|grep TIME_WAIT|awk '{print $5}'|sort|uniq -c|sort -rn|head -n20 ``` # 监控linux性能命令 ## top ``` 按大写的 F 或 O 键,然后按 a-z 可以将进程按照相应的列进行排序, 然后回车。而大写的 R 键可以将当前的排序倒转 ``` | 列名 | 含义 | | ------- | ------------------------------------------------------------ | | PID | 进程id | | PPID | 父进程id | | RUSER | Real user name | | UID | 进程所有者的用户id | | USER | 进程所有者的用户名 | | GROUP | 进程所有者的组名 | | TTY | 启动进程的终端名。不是从终端启动的进程则显示为 ? | | PR | 优先级 | | NI | nice值。负值表示高优先级,正值表示低优先级 | | P | 最后使用的CPU,仅在多CPU环境下有意义 | | %CPU | 上次更新到现在的CPU时间占用百分比 | | TIME | 进程使用的CPU时间总计,单位秒 | | TIME+ | 进程使用的CPU时间总计,单位1/100秒 | | %MEM | 进程使用的物理内存百分比 | | VIRT | 进程使用的虚拟内存总量,单位kb。VIRT=SWAP+RES | | SWAP | 进程使用的虚拟内存中,被换出的大小,单位kb。 | | RES | 进程使用的、未被换出的物理内存大小,单位kb。RES=CODE+DATA | | CODE | 可执行代码占用的物理内存大小,单位kb | | DATA | 可执行代码以外的部分(数据段+栈)占用的物理内存大小,单位kb | | SHR | 共享内存大小,单位kb | | nFLT | 页面错误次数 | | nDRT | 最后一次写入到现在,被修改过的页面数。 | | S | 进程状态。D=不可中断的睡眠状态,R=运行,S=睡眠,T=跟踪/停止,Z=僵尸进程 | | COMMAND | 命令名/命令行 | | WCHAN | 若该进程在睡眠,则显示睡眠中的系统函数名 | | Flags | 任务标志,参考 sched.h | ## dmesg,查看系统日志 ``` dmesg ``` ## iostat,磁盘IO情况监控 ``` iostat -xz 1 # r/s, w/s, rkB/s, wkB/s:分别表示每秒读写次数和每秒读写数据量(千字节)。读写量过大,可能会引起性能问题。 # await:IO操作的平均等待时间,单位是毫秒。这是应用程序在和磁盘交互时,需要消耗的时间,包括IO等待和实际操作的耗时。如果这个数值过大,可能是硬件设备遇到了瓶颈或者出现故障。 # avgqu-sz:向设备发出的请求平均数量。如果这个数值大于1,可能是硬件设备已经饱和(部分前端硬件设备支持并行写入)。 # %util:设备利用率。这个数值表示设备的繁忙程度,经验值是如果超过60,可能会影响IO性能(可以参照IO操作平均等待时间)。如果到达100%,说明硬件设备已经饱和。 # 如果显示的是逻辑设备的数据,那么设备利用率不代表后端实际的硬件设备已经饱和。值得注意的是,即使IO性能不理想,也不一定意味这应用程序性能会不好,可以利用诸如预读取、写缓存等策略提升应用性能。 ``` ## free,内存使用情况 ``` free -m eg: total used free shared buffers cached Mem: 1002 769 232 0 62 421 -/+ buffers/cache: 286 715 Swap: 1153 0 1153 第一部分Mem行: total 内存总数: 1002M used 已经使用的内存数: 769M free 空闲的内存数: 232M shared 当前已经废弃不用,总是0 buffers Buffer 缓存内存数: 62M cached Page 缓存内存数:421M 关系:total(1002M) = used(769M) + free(232M) 第二部分(-/+ buffers/cache): (-buffers/cache) used内存数:286M (指的第一部分Mem行中的used – buffers – cached) (+buffers/cache) free内存数: 715M (指的第一部分Mem行中的free + buffers + cached) 可见-buffers/cache反映的是被程序实实在在吃掉的内存,而+buffers/cache反映的是可以挪用的内存总数. 第三部分是指交换分区 ``` ## sar,查看网络吞吐状态 ``` # sar命令在这里可以查看网络设备的吞吐率。在排查性能问题时,可以通过网络设备的吞吐量,判断网络设备是否已经饱和 sar -n DEV 1 # # sar命令在这里用于查看TCP连接状态,其中包括: # active/s:每秒本地发起的TCP连接数,既通过connect调用创建的TCP连接; # passive/s:每秒远程发起的TCP连接数,即通过accept调用创建的TCP连接; # retrans/s:每秒TCP重传数量; # TCP连接数可以用来判断性能问题是否由于建立了过多的连接,进一步可以判断是主动发起的连接,还是被动接受的连接。TCP重传可能是因为网络环境恶劣,或者服务器压力过大导致丢包 sar -n TCP,ETCP 1 ``` ## vmstat, 给定时间监控CPU使用率, 内存使用, 虚拟内存交互, IO读写 ``` # 2表示每2秒采集一次状态信息, 1表示只采集一次(忽略既是一直采集) vmstat 2 1 eg: r b swpd free buff cache si so bi bo in cs us sy id wa 1 0 0 3499840 315836 3819660 0 0 0 1 2 0 0 0 100 0 0 0 0 3499584 315836 3819660 0 0 0 0 88 158 0 0 100 0 0 0 0 3499708 315836 3819660 0 0 0 2 86 162 0 0 100 0 0 0 0 3499708 315836 3819660 0 0 0 10 81 151 0 0 100 0 1 0 0 3499732 315836 3819660 0 0 0 2 83 154 0 0 100 0 ``` - **r** 表示运行队列(就是说多少个进程真的分配到CPU),我测试的服务器目前CPU比较空闲,没什么程序在跑,当这个值超过了CPU数目,就会出现CPU瓶颈了。这个也和top的负载有关系,一般负载超过了3就比较高,超过了5就高,超过了10就不正常了,服务器的状态很危险。top的负载类似每秒的运行队列。如果运行队列过大,表示你的CPU很繁忙,一般会造成CPU使用率很高。 - **b** 表示阻塞的进程,这个不多说,进程阻塞,大家懂的。 - **swpd** 虚拟内存已使用的大小,如果大于0,表示你的机器物理内存不足了,如果不是程序内存泄露的原因,那么你该升级内存了或者把耗内存的任务迁移到其他机器。 - **free** 空闲的物理内存的大小,我的机器内存总共8G,剩余3415M。 - **buff** Linux/Unix系统是用来存储,目录里面有什么内容,权限等的缓存,我本机大概占用300多M - **cache** cache直接用来记忆我们打开的文件,给文件做缓冲,我本机大概占用300多M(这里是Linux/Unix的聪明之处,把空闲的物理内存的一部分拿来做文件和目录的缓存,是为了提高 程序执行的性能,当程序使用内存时,buffer/cached会很快地被使用。) - **si** 每秒从磁盘读入虚拟内存的大小,如果这个值大于0,表示物理内存不够用或者内存泄露了,要查找耗内存进程解决掉。我的机器内存充裕,一切正常。 - **so** 每秒虚拟内存写入磁盘的大小,如果这个值大于0,同上。 - **bi** 块设备每秒接收的块数量,这里的块设备是指系统上所有的磁盘和其他块设备,默认块大小是1024byte,我本机上没什么IO操作,所以一直是0,但是我曾在处理拷贝大量数据(2-3T)的机器上看过可以达到140000/s,磁盘写入速度差不多140M每秒 - **bo** 块设备每秒发送的块数量,例如我们读取文件,bo就要大于0。bi和bo一般都要接近0,不然就是IO过于频繁,需要调整。 - **in** 每秒CPU的中断次数,包括时间中断 - **cs** 每秒上下文切换次数,例如我们调用系统函数,就要进行上下文切换,线程的切换,也要进程上下文切换,这个值要越小越好,太大了,要考虑调低线程或者进程的数目,例如在apache和nginx这种web服务器中,我们一般做性能测试时会进行几千并发甚至几万并发的测试,选择web服务器的进程可以由进程或者线程的峰值一直下调,压测,直到cs到一个比较小的值,这个进程和线程数就是比较合适的值了。系统调用也是,每次调用系统函数,我们的代码就会进入内核空间,导致上下文切换,这个是很耗资源,也要尽量避免频繁调用系统函数。上下文切换次数过多表示你的CPU大部分浪费在上下文切换,导致CPU干正经事的时间少了,CPU没有充分利用,是不可取的。 - **us** 用户CPU时间,我曾经在一个做加密解密很频繁的服务器上,可以看到us接近100,r运行队列达到80(机器在做压力测试,性能表现不佳)。 - **sy** 系统CPU时间,如果太高,表示系统调用时间长,例如是IO操作频繁。 - **id** 空闲 CPU时间,一般来说,id + us + sy = 100,一般我认为id是空闲CPU使用率,us是用户CPU使用率,sy是系统CPU使用率。 - **wt** 等待IO CPU时间。
-
【功能模块】robox mesa【操作步骤&问题现象】1、按照步骤跑起来安装跑起来robox2、通过cat /proc/xorgpid号/maps | grep mesa 发现用的是/usr/lib/aarch64-linux-gnu/libEGL_mesa.so.0.0.0的库,并没有调用手动编译mesa然后安装到/usr/local/lib目录下mesa相关的so3、通过objdump -x /usr/lib/aarch64-linux-gnu/libEGL_mesa.so.0.0.0查看,也没有调用到/usr/local/lib目录下mesa相关的so4、问题是:怎样才能调用到mesa-19.0.8源码编译出来的so?【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
问题现象:分段扩容,重分步阶段UDF业务报错问题版本:HC DWS 8.1.1.202问题原因:udf相关作业中部分表未重分布,部分表已完成重分布,此种场景会跨nodegroup,会产生额外的stream线程,导致线程数过多,作业中涉及udf函数的线程增加最200左右的udf线程,从而业务报错。规避方法:手动将报错中的表、视图中的表手动提前进行重分布,在手动拉起重分步1.获取业务涉及的需要重分步的表比如获取视图中的表explain (stats on) 语句 可以获取到作业中所有的表cat xxxx |grep -i 'create table'|sort |uniqselect oid::regclass from pg_class where relname in (xxxx,xxxx);找到对应的表 按如下格式存成文本(没有列头)库名 schema 表名vmall_oar vmall_oar_cs behavior_content_liveactivityvmall_oar dwrdim dwr_dim_sku_dvmall_oar dwrdim dwr_dim_sys_ssid_cfgvmall_oar vmall_oc_0 ord_activity2.手动执行重分布gs_redis -p 8000 -d postgres -j 7 -r -m read-only -P /home/Ruby/aaa.txt3.待这些表重分布完成后,手动调起重分布nohup gs_expand -t redistribute --fast-redis --parallel-jobs=7 --redis-mode=insert &
-
【功能模块】开发环境:Ubuntu18.04,MindStudio3.0.4运行环境:Atlas200DK【操作步骤&问题现象】1、命令行运行人脸识别样例报错。2、MindStudio运行让脸识别样例报错。3、报错都是线程错误。【截图信息】重启Atlas还是报这个错。
-
上一篇:【Go实现】实践GoF的23种设计模式:SOLID原则简单的分布式应用系统(示例代码工程):https://github.com/ruanrunxue/Practice-Design-Pattern–Go-Implementation简述GoF 对单例模式(Singleton)的定义如下:Ensure a class only has one instance, and provide a global point of access to it.也即,保证一个类只有一个实例,并且为它提供一个全局访问点。在程序设计中,有些对象通常只需要一个共享的实例,比如线程池、全局缓存、对象池等。实现共享实例最简单直接的方式就是全局变量。但是,使用全局变量会带来一些问题,比如:客户端程序可以创建同类实例,从而无法保证在整系统上只有一个共享实例。难以控制对象的访问,比如想增加一个“访问次数统计”的功能就很难,可扩展性较低。把实现细节暴露给客户端程序,加深了耦合,容易产生霰弹式修改。对这种全局唯一的场景,更好的是使用单例模式去实现。单例模式能够限制客户端程序创建同类实例,并且可以在全局访问点上扩展或修改功能,而不影响客户端程序。但是,并非所有的全局唯一都适用单例模式。比如下面这种场景:考虑需要统计一个API调用的情况,有两个指标,成功调用次数和失败调用次数。这两个指标都是全局唯一的,所以有人可能会将其建模成两个单例SuccessApiMetric和FailApiMetric。按照这个思路,随着指标数量的增多,你会发现代码里类的定义会越来越多,也越来越臃肿。这也是单例模式最常见的误用场景,更好的方法是将两个指标设计成一个对象ApiMetric下的两个实例ApiMetic success和ApiMetic fail。那么,如何判断一个对象是否应该被建模成单例?通常,被建模成单例的对象都有“中心点”的含义,比如线程池就是管理所有线程的中心。所以,在判断一个对象是否适合单例模式时,先思考下,是一个中心点吗?UML结构代码实现根据单例模式的定义,实现的关键点有两个:限制调用者直接实例化该对象;为该对象的单例提供一个全局唯一的访问方法。对于 C++ / Java 而言,只需把对象的构造函数设计成私有的,并提供一个 static 方法去访问该对象的唯一实例即可。但 Go 语言并没有构造函数的概念,也没有 static 方法,所以需要另寻出路。我们可以利用 Go 语言 package 的访问规则来实现,将单例对象设计成首字母小写,这样就能限定它的访问范围只在当前package下,模拟了 C++ / Java 的私有构造函数;然后,在当前 package 下实现一个首字母大写的访问函数,也就相当于 static 方法的作用了。示例在简单的分布式应用系统(示例代码工程)中,我们定义了一个网络模块 network,模拟实现了网络报文转发功能。network 的设计也很简单,通过一个哈希表维持了 Endpoint 到 Socket 的映射,报文转发时,通过 Endpoint 寻址到 Socket,再调用 Socket 的 Receive 方法完成转发。因为整系统只需一个 network 对象,而且它在领域模型中具有中心点的语义,所以我们很自然地使用单例模式来实现它。单例模式大致可以分成两类,“饿汉模式”和“懒汉模式”。前者是在系统初始化期间就完成了单例对象的实例化;后者则是在调用时才进行延迟实例化,从而一定程度上节省了内存。“饿汉模式”实现// demo/network/network.go package network // 1、设计为小写字母开头,表示只在network包内可见,限制客户端程序的实例化 type network struct { sockets sync.Mapvar instancevar instance } // 2、定义一个包内可见的实例对象,也即单例 var instance = &network{sockets: sync.Map{}} // 3、定义一个全局可见的唯一访问方法 func Instance() *network { return instance } func (n *network) Listen(endpoint Endpoint, socket Socket) error { if _, ok := n.sockets.Load(endpoint); ok { return ErrEndpointAlreadyListened } n.sockets.Store(endpoint, socket) return nil } func (n *network) Send(packet *Packet) error { record, rOk := n.sockets.Load(packet.Dest()) socket, sOk := record.(Socket) if !rOk || !sOk { return ErrConnectionRefuse } go socket.Receive(packet) return nil }那么,客户端就可以通过 network.Instance() 引用该单例了:// demo/sidecar/flowctrl_sidecar.go package sidecar type FlowCtrlSidecar struct {...} // 通过 network.Instance() 直接引用单例 func (f *FlowCtrlSidecar) Listen(endpoint network.Endpoint) error { return network.Instance().Listen(endpoint, f) } ...“懒汉模式”实现众所周知,“懒汉模式”会带来线程安全问题,可以通过普通加锁,或者更高效的双重检验加锁来优化。不管是哪种方法,都是为了保证单例只会被初始化一次。type network struct {...} // 单例 var instance *network // 定义互斥锁 var mutex = sync.Mutex{} // 普通加锁,缺点是每次调用 Instance() 都需要加锁 func Instance() *network { mutex.Lock() if instance == nil { instance = &network{sockets: sync.Map{}} } mutex.Unlock() return instance } // 双重检验后加锁,实例化后无需加锁 func Instance() *network { if instance == nil { mutex.Lock() if instance == nil { instance = &network{sockets: sync.Map{}} } mutex.Unlock() } return instance }对于“懒汉模式”,Go 语言还有一个更优雅的实现方式,那就是利用 sync.Once。它有一个 Do 方法,方法声明为 func (o *Once) Do(f func()),其中入参是 func() 的方法类型,Go 会保证该方法仅会被调用一次。利用这个特性,我们就能够实现单例只被初始化一次了。type network struct {...} // 单例 var instance *network // 定义 once 对象 var once = sync.Once{} // 通过once对象确保instance只被初始化一次 func Instance() *network { once.Do(func() { // 只会被调用一次 instance = &network{sockets: sync.Map{}} }) return instance }扩展提供多个实例虽然单例模式从定义上表示每个对象只能有一个实例,但是我们不应该被该定义限制住,还得从模式本身的动机来去理解它。单例模式的一大动机是限制客户端程序对对象进行实例化,至于实例有多少个其实并不重要,根据具体场景来进行建模、设计即可。比如在前面的 network 模块中,现在新增一个这样的需求,将网络拆分为互联网和局域网。那么,我们可以这么设计:type network struct {...} // 定义互联网单例 var inetInstance = &network{sockets: sync.Map{}} // 定义局域网单例 var lanInstance = &network{sockets: sync.Map{}} // 定义互联网全局可见的唯一访问方法 func Internet() *network { return inetInstance } // 定义局域网全局可见的唯一访问方法 func Lan() *network { return lanInstance }虽然上述例子中,network 结构有两个实例,但是本质上还是单例模式,因为它做到了限制客户端实例化,以及为每个单例提供了全局唯一的访问方法。提供多种实现单例模式也可以实现多态,如果你预测该单例未来可能会扩展,那么就可以将它设计成抽象的接口,让客户端依赖抽象,这样,未来扩展时就无需改动客户端程序了。比如,我们可以 network 设计为一个抽象接口:// network 抽象接口 type network interface { Listen(endpoint Endpoint, socket Socket) error Send(packet *Packet) error } // network 的实现1 type networkImpl1 struct { sockets sync.Map } func (n *networkImpl1) Listen(endpoint Endpoint, socket Socket) error {...} func (n *networkImpl1) Send(packet *Packet) error {...} // networkImpl1 实现的单例 var instance = &networkImpl1{sockets: sync.Map{}} // 定义全局可见的唯一访问方法,注意返回值时network抽象接口! func Instance() network { return instance } // 客户端使用示例 func client() { packet := network.NewPacket(srcEndpoint, destEndpoint, payload) network.Instance().Send(packet) }如果未来需要新增一种 networkImpl2 实现,那么我们只需修改 instance 的初始化逻辑即可,客户端程序无需改动:// 新增network 的实现2 type networkImpl2 struct {...} func (n *networkImpl2) Listen(endpoint Endpoint, socket Socket) error {...} func (n *networkImpl2) Send(packet *Packet) error {...} // 将单例 instance 修改为 networkImpl2 实现 var instance = &networkImpl2{...} // 单例全局访问方法无需改动 func Instance() network { return instance } // 客户端使用也无需改动 func client() { packet := network.NewPacket(srcEndpoint, destEndpoint, payload) network.Instance().Send(packet) }有时候,我们还可能需要通过读取配置来决定使用哪种单例实现,那么,我们可以通过 map 来维护所有的实现,然后根据具体配置来选取对应的实现:// network 抽象接口 type network interface { Listen(endpoint Endpoint, socket Socket) error Send(packet *Packet) error } // network 具体实现 type networkImpl1 struct {...} type networkImpl2 struct {...} type networkImpl3 struct {...} type networkImpl4 struct {...} // 单例 map var instances = make(map[string]network) // 初始化所有的单例 func init() { instances["impl1"] = &networkImpl1{...} instances["impl2"] = &networkImpl2{...} instances["impl3"] = &networkImpl3{...} instances["impl4"] = &networkImpl4{...} } // 全局单例访问方法,通过读取配置决定使用哪种实现 func Instance() network { impl := readConf() instance, ok := instances[impl] if !ok { panic("instance not found") } return instance }典型应用场景日志。每个服务通常都会需要一个全局的日志对象来记录本服务产生的日志。全局配置。对于一些全局的配置,可以通过定义一个单例来供客户端使用。唯一序列号生成。唯一序列号生成必然要求整系统只能有一个生成实例,非常合适使用单例模式。线程池、对象池、连接池等。xxx池的本质就是共享,也是单例模式的常见场景。全局缓存…优缺点优点在合适的场景,使用单例模式有如下的优点:整系统只有一个或几个实例,有效节省了内存和对象创建的开销。通过全局访问点,可以方便地扩展功能,比如新增加访问次数的统计。对客户端隐藏实现细节,可避免霰弹式修改。缺点虽然单例模式相比全局变量有诸多的优点,但它本质上还是一个“全局变量”,还是避免不了全局变量的一些缺点:函数调用的隐式耦合。通常我们都期望从函数的声明中就能知道该函数做了什么、依赖了什么、返回了什么。使用使用单例模式就意味着,无需通过函数传参,就能够在函数中使用该实例。也即将依赖/耦合隐式化了,不利于更好地理解代码。对测试不友好。通常对一个方法/函数进行测试,我们并不需要知道它的具体实现。但如果方法/函数中有使用单例对象,我们就不得不考虑单例状态的变化了,也即需要考虑方法/函数的具体实现了。并发问题。共享就意味着可能存在并发问题,我们不仅需要在初始化阶段考虑并发问题,在初始化后更是要时刻注意。因此,在高并发的场景,单例模式也可能存在锁冲突问题。单例模式虽然简单易用,但也是最容易被滥用的设计模式。它并不是“银弹”,在实际使用时,还需根据具体的业务场景谨慎使用。与其他模式的关联工厂方法模式、抽象工厂模式很多时候都会以单例模式来实现,因为工厂类通常是无状态的,而且全局只需一个实例即可,能够有效避免对象的频繁创建和销毁。
-
【功能模块】硬件:Atlas500小站和Atlas 200 NPU;开发SDK:Ascend-cann-toolkit_20.2.rc1_linux-x86_64,protobuf 3.11+,OpenCV3;【操作步骤&问题现象】1、使用以上软硬件环境开发YoloV5实时识别的多线程程序,在可能出现异常的地方添加try/catch语法,捕获异常并打印到控制台;2、部署到小站docker容器中,运行测试;3、线程出现异常,但是线程自动重启,没有打印出异常信息:4、为了能debug存在的问题,请问如何捕获异常并打印,或者shell操作得到异常信息(类似docker container层面的docker run CONTAINERNAME; echo $?)?【截图信息】【日志信息】(可选,上传日志内容或者附件)
-
需要使用C++的thread库,请问可以在CMakeLists中加-pthread吗
-
【功能模块】python写多进程处理时,利用 multiprocessing 中的 Manager 定义全局变量会导致线上报错:程序运行失败。【操作步骤&问题现象】1、线上提交如下代码:from multiprocessing import ManagerOUTPUT_PATH = "/output/solution.txt"if __name__ == '__main__': a = Manager().dict() with open(OUTPUT_PATH, "w") as f: pass会显示程序运行失败。2、但是将定义语句去掉,即:from multiprocessing import ManagerOUTPUT_PATH = "/output/solution.txt"if __name__ == '__main__': # a = Manager().dict() with open(OUTPUT_PATH, "w") as f: pass则不会显示程序运行失败,而是显示输出格式错误。【问题描述】上述操作都是在python自带的包中进行的。这说明在利用Manager定义多进程全局变量的时候会导致线上环境报错,而在线下是不会出现报错问题的。请问线上环境是出现这种报错是什么原因呢?
-
可能原因:安装过SysChecker未卸载干净,端口被占用1.检查SysChecker安装是否OMS主节点2.查看相关日志/opt/SysChecker/Output/SysCheckerRestLog.txt,发现报错信息Address already used3.检查端口24443是否被占用,4.netstat -ano|grep 24443发现24443端口被占用,SysChecker未卸载干净,有进程残留5.卸载Syschecker后,重新安装Syschecker对接环境 这个基本上是Syschecker出问题 由于现场不能使用root直接登录,所以在填入对接信息的时候不需要写入root密码,但是需要手动在主OMS节点安装Syschecker 重新安装后还是相同的问题,需要查看相关日志/opt/SysChecker/Output/SysCheckerRestLog.txt 发现报错信息 Address already used 判断可能是端口被占用了 netstat -anp| grep 24443 确认端口被占用。 杀掉占用端口的进程后,重新启动或重启安装Syschecker后问题解决。
-
@TOC五种IO模型阻塞IO阻塞IO: 在内核将数据准备好之前, 系统调用会一直等待. 所有的套接字, 默认都是阻塞方式例如:A在钓鱼时,会一直盯着鱼漂,当鱼漂动时,拉动鱼竿,其余的时间都在盯着鱼漂。非阻塞IO非阻塞IO: 如果内核还未将数据准备好, 系统调用仍然会直接返回, 并且返回EWOULDBLOCK错误码非阻塞IO往往需要程序员循环的方式反复尝试读写文件描述符, 这个过程称为轮询. 这对CPU来说是较大的浪费, 一般只有特定场景下才使用。例如:B也来钓鱼,B一遍玩手机一遍看着鱼漂,还时不时的问A钓没钓到鱼。B不是一直在等还可以玩手机的等。信号驱动信号驱动IO: 内核将数据准备好的时候, 使用SIGIO信号通知应用程序进行IO操作例:此时C也来钓鱼,C将鱼竿上系了一个铃铛,C在看书,当铃铛响了C开始拉动鱼竿。C就像是信号驱动IO一样。多路转接IO多路转接: 虽然从流程图上看起来和阻塞IO类似. 实际上最核心在于IO多路转接能够同时等待多个文件描述符的就绪状态例:此时D拿着20根鱼竿来钓鱼,D来回看着就可以了。20根鱼竿鱼上钩的几率远大于之前的3个人。异步IO异步IO: 由内核在数据拷贝完成时, 通知应用程序(而信号驱动是告诉应用程序何时可以开始拷贝数据).例:E带着手下来钓鱼,E说:你在这钓,把桶钓满了拿回来给我。E然后开车走了。E根本不用在这里等待。总结:任何IO都包括2个步骤:1是等,2是拷贝读IO=等待读事件就绪+内核数据拷贝到用户空间写IO=等待写事件就绪+用户空间数据拷贝到内核空间高级IO的本质:尽可能减少等的时间比重同步和异步通信同步概念:所谓同步,就是在发出一个调用时,在没有得到结果之前,该调用就不返回. 但是一旦调用返回,就得到返回值了; 换句话说,就是由调用者主动等待这个调用的结果异步概念:异步则是相反,调用在发出之后,这个调用就直接返回了,所以没有返回结果; 换句话说,当一个异步过程调用发出后,调用者不会立刻得到结果; 而是在调用发出后,被调用者通过状态、通知来通知调用者,或通过回调函数处理这个调用还需注意多进程和多线程同步和异步之间的区别:进程/线程同步也是进程/线程之间直接的制约关系是为完成某种任务而建立的两个或多个线程,这个线程需要在某些位置上协调他们的工作次序而等待、传递信息所产生的制约关系. 尤其是在访问临界资源的时候。I/O多路转接之select系统提供select函数来实现多路复用输入/输出模型.select系统调用是用来让我们的程序监视多个文件描述符的状态变化的程序会停在select这里等待,直到被监视的文件描述符有一个或多个发生了状态改变select函数原型参数说明:参数nfds是需要监视的最大的文件描述符值+1;rdset,wrset,exset分别对应于需要检测的可读文件描述符的集合,可写文件描述符的集 合及异常文件描述符的集合;参数timeout为结构timeval,用来设置select()的等待时间参数timeout取值:NULL:则表示select()没有timeout,select将一直被阻塞,直到某个文件描述符上发生了件;0:仅检测描述符集合的状态,然后立即返回,并不等待外部事件的发生。特定的时间值:如果在指定的时间段里没有事件发生,select将超时返回。fd_set结构用命令vim /usr/include/sys/select.h查看源码:select的过程:简单的select服务器将创建套接字单独封装成一个类Sock.hppselectServer.hppselect.cc下面来测试一下:在用手机测试一下:总结select就绪条件读就绪socket内核中, 接收缓冲区中的字节数, 大于等于低水位标记SO_RCVLOWAT. 此时可以无阻塞的读该文件描述符, 并且返回值大于0;socket TCP通信中, 对端关闭连接, 此时对该socket读, 则返回0;监听的socket上有新的连接请求;socket上有未处理的错误写就绪socket内核中, 发送缓冲区中的可用字节数(发送缓冲区的空闲位置大小), 大于等于低水位标记SO_SNDLOWAT, 此时可以无阻塞的写, 并且返回值大于0socket的写操作被关闭(close或者shutdown). 对一个写操作被关闭的socket进行写操作, 会触发SIGPIPE信号;socket使用非阻塞connect连接成功或失败之后;socket上有未读取的错误;select特点可监控的文件描述符个数取决与sizeof(fd_set)的值.服务器上sizeof(fd_set)=512,每bit表示一个文件描述符,则服务器上支持的最大文件描述符是512*8=4096.将fd加入select监控集的同时,还要再使用一个数据结构array保存放到select监控集中的fd,一是用于再select 返回后,array作为源数据和fd_set进行FD_ISSET判断。二是select返回后会把以前加入的但并无事件发生的fd清空,则每次开始select前都要重新从array取得fd逐一加入(FD_ZERO最先),扫描array的同时取得fd最大值maxfd,用于select的第一个参数。select缺点每次调用select, 都需要手动设置fd集合, 从接口使用角度来说也非常不便.每次调用select,都需要把fd集合从用户态拷贝到内核态,这个开销在fd很多时会很大同时每次调用select都需要在内核遍历传递进来的所有fd,这个开销在fd很多时也很大select支持的文件描述符数量太小本篇文章到这就结束了,后面还会有poll,epoll的讲解。
-
使用GeneratorDataset加载自定义数据集时,会根据num_parallel_workers来启动多线程或多线程来加载数据集,每个进程或者线程中都会尝试去打开数据集文件。Linux一般默认允许同时打开的文件数为1024,当并发度过高 (num_parallel_workers)并且数据集文件较多,可能会导致出现 OSERROR: [Errno 24] Too many open files。此时可以在shell环境上执行ulimit -n 65535来调整同时打开文件的阈值。另外对Dataset对象进行“非常规”的操作,也有可能导致 Too many open files 错误。 1 import numpy as np 2 import mindspore.dataset as ds 3 import argparse 4 from mindspore import context 5 from datasets.online_adaptation_dataset import OnlineAdaptationDataset, OnlineAdaptationDatasetStorage 6 7 class MyAccessible: 8 def __init__(self): 9 self._data = np.random.sample((10, 2)) 10 self._label = np.random.sample((10, 1)) 11 12 def __getitem__(self, index): 13 return self._data[index], self._label[index] 14 15 def __len__(self): 16 return len(self._data) 17 18 class MyData: 19 def __init__(self, data): 20 self.data = data 21 22 def __getitem__(self, index): 23 return self.data.__getitem__(index) 24 25 if __name__ == '__main__': 26 parser = argparse.ArgumentParser(description='MindSpore LeNet Example') 27 parser.add_argument('--device_target', type=str, default="Ascend", choices=['Ascend', 'GPU', 'CPU']) 28 29 args = parser.parse_known_args()[0] 30 context.set_context(device_id=4, mode=context.PYNATIVE_MODE, device_target=args.device_target) 31 for i in range(10): 32 data = MyAccessible() 33 dataset = ds.GeneratorDataset(source=MyData(data), column_names=["data", "label"],num_parallel_workers=4, shuffle=True) 34 dataset = dataset.batch(batch_size=2) 35 import pdb;pdb.set_trace() 36 datas = iter(dataset) 37 print(next(datas)) 38 datas = iter(dataset)脚本中第36行使用iter()来创建迭代器对象后,在第37行中使用next(datas)读取迭代器中第一个元素,然后在第38行重复使用iter()来创建迭代器。原因分析:1. Mindspore提供了create_dict_iterator()和create_tuple_iterator(),分别返回dict迭代器和tuple迭代器,不建议直接使用iter()方法来生成迭代器。create_dict_iterator()以及create_tuple_iterator(),可以设置num_epochs参数来控制迭代器的迭代次数,以及output_numpy参数来控制输出数据类型为numpy.ndarray或者Tensor。2. 重复调用iter()会重复创建迭代器,而GeneratorDataset加载数据集时默认为多进程加载,每次打开的句柄在主进程停止前得不到释放,导致打开句柄数一直在增长。解决方法:第36行修改为 datas=dataset.create_tuple_iterator(num_epochs=1, output_numpy=True)移除第38行
-
这篇总结主要是基于我之前JVM系列文章而形成的的。主要是把重要的知识点用自己的话说了一遍,可能会有一些错误,还望见谅和指点。谢谢#更多详细内容可以查看我的专栏文章:深入理解JVM虚拟机https://blog.csdn.net/column/details/21960.htmlJVM介绍和源码首先JVM是一个虚拟机,当你安装了jre,它就包含了jvm环境。JVM有自己的内存结构,字节码执行引擎,因此class字节码才能在jvm上运行,除了Java以外,Scala,groovy等语言也可以编译成字节码而后在jvm中运行。JVM是用c开发的。JVM内存模型内存模型老生常谈了,主要就是线程共享的堆区,方法区,本地方法栈。还有线程私有的虚拟机栈和程序计数器。堆区存放所有对象,每个对象有一个地址,Java类jvm初始化时加载到方法区,而后会在堆区中生成一个Class对象,来负责这个类所有实例的实例化。栈区存放的是栈帧结构,栈帧是一段内存空间,包括参数列表,返回地址,局部变量表等,局部变量表由一堆slot组成,slot的大小固定,根据变量的数据类型决定需要用到几个slot。方法区存放类的元数据,将原来的字面量转换成引用,当然,方法区也提供常量池,常量池存放-128到127的数字类型的包装类。字符串常量池则会存放使用intern的字符串变量。JVM OOM和内存泄漏这里指的是oom和内存泄漏这类错误。oom一般分为三种,堆区内存溢出,栈区内存溢出以及方法区内存溢出。堆内存溢出主要原因是创建了太多对象,比如一个集合类死循环添加一个数,此时设置jvm参数使堆内存最大值为10m,一会就会报oom异常。栈内存溢出主要与栈空间和线程有关,因为栈是线程私有的,如果创建太多线程,内存值超过栈空间上限,也会报oom。方法区内存溢出主要是由于动态加载类的数量太多,或者是不断创建一个动态代理,用不了多久方法区内存也会溢出,会报oom,这里在1.7之前会报permgem oom,1.8则会报meta space oom,这是因为1.8中删除了堆中的永久代,转而使用元数据区。内存泄漏一般是因为对象被引用无法回收,比如一个集合中存着很多对象,可能你在外部代码把对象的引用置空了,但是由于对象还被集合给引用着,所以无法被回收,导致内存泄漏。测试也很简单,就在集合里添加对象,添加完以后把引用置空,循环操作,一会就会出现oom异常,原因是内存泄漏太多了,导致没有空间分配新的对象。常见调试工具命令行工具有jstack jstat jmap 等,jstack可以跟踪线程的调用堆栈,以便追踪错误原因。jstat可以检查jvm的内存使用情况,gc情况以及线程状态等。jmap用于把堆栈快照转储到文件系统,然后可以用其他工具去排查。visualvm是一款很不错的gui调试工具,可以远程登录主机以便访问其jvm的状态并进行监控。class文件结构class文件结构比较复杂,首先jvm定义了一个class文件的规则,并且让jvm按照这个规则去验证与读取。开头是一串魔数,然后接下来会有各种不同长度的数据,通过class的规则去读取这些数据,jvm就可以识别其内容,最后将其加载到方法区。JVM的类加载机制jvm的类加载顺序是bootstrap类加载器,extclassloader加载器,最后是appclassloader用户加载器,分别加载的是jdk/bin ,jdk/ext以及用户定义的类目录下的类(一般通过ide指定),一般核心类都由bootstrap和ext加载器来加载,appclassloader用于加载自己写的类。双亲委派模型,加载一个类时,首先获取当前类加载器,先找到最高层的类加载器bootstrap让他尝试加载,他如果加载不了再让ext加载器去加载,如果他也加载不了再让appclassloader去加载。这样的话,确保一个类型只会被加载一次,并且以高层类加载器为准,防止某些类与核心类重复,产生错误。defineclass findclass和loadclass类加载classloader中有两个方法loadclass和findclass,loadclass遵从双亲委派模型,先调用父类加载的loadclass,如果父类和自己都无法加载该类,则会去调用findclass方法,而findclass默认实现为空,如果要自定义类加载方式,则可以重写findclass方法。常见使用defineclass的情况是从网络或者文件读取字节码,然后通过defineclass将其定义成一个类,并且返回一个Class对象,说明此时类已经加载到方法区了。当然1.8以前实现方法区的是永久代,1.8以后则是元空间了。JVM虚拟机字节码执行引擎jvm通过字节码执行引擎来执行class代码,他是一个栈式执行引擎。这部分内容比较高深,在这里就不献丑了。编译期优化和运行期优化编译期优化主要有几种1 泛型的擦除,使得泛型在编译时变成了实际类型,也叫伪泛型。2 自动拆箱装箱,foreach循环自动变成迭代器实现的for循环。3 条件编译,比如if(true)直接可得。运行期优化主要有几种1 JIT即时编译Java既是编译语言也是解释语言,因为需要编译代码生成字节码,而后通过解释器解释执行。但是,有些代码由于经常被使用而成为热点代码,每次都编译太过费时费力,干脆直接把他编译成本地代码,这种方式叫做JIT即时编译处理,所以这部分代码可以直接在本地运行而不需要通过jvm的执行引擎。2 公共表达式擦除,就是一个式子在后面如果没有被修改,在后面调用时就会被直接替换成数值。3 数组边界擦除,方法内联,比较偏,意义不大。4 逃逸分析,用于分析一个对象的作用范围,如果只局限在方法中被访问,则说明不会逃逸出方法,这样的话他就是线程安全的,不需要进行并发加锁。1JVM的垃圾回收1 GC算法:停止复制,存活对象少时适用,缺点是需要两倍空间。标记清除,存活对象多时适用,但是容易产生随便。标记整理,存活对象少时适用,需要移动对象较多。2 GC分区,一般GC发生在堆区,堆区可分为年轻代,老年代,以前有永久代,现在没有了。年轻代分为eden和survior,新对象分配在eden,当年轻代满时触发minor gc,存活对象移至survivor区,然后两个区互换,等待下一场gc,当对象存活的阈值达到设定值时进入老年代,大对象也会直接进入老年代。老年代空间较大,当老年代空间不足以存放年轻代过来的对象时,开始进行full gc。同时整理年轻代和老年代。一般年轻代使用停止复制,老年代使用标记清除。3 垃圾收集器serial串行parallel并行它们都有年轻代与老年代的不同实现。然后是scanvage收集器,注重吞吐量,可以自己设置,不过不注重延迟。cms垃圾收集器,注重延迟的缩短和控制,并且收集线程和系统线程可以并发。cms收集步骤主要是,初次标记gc root,然后停顿进行并发标记,而后处理改变后的标记,最后停顿进行并发清除。g1收集器和cms的收集方式类似,但是g1将堆内存划分成了大小相同的小块区域,并且将垃圾集中到一个区域,存活对象集中到另一个区域,然后进行收集,防止产生碎片,同时使分配方式更灵活,它还支持根据对象变化预测停顿时间,从而更好地帮用户解决延迟等问题。
-
前篇可以参考:MindSpore数据集加载-GeneratorDataset加载自定义数据集典型错误(1)本篇继续分析GeneratorDataset加载自定义数据集典型错误,用户脚本如下: 1 class Kinetics: 2 """ 3 Args: 4 root (string): Root directory path. 5 spatial_transform (callable, optional): A function/transform that takes in an PIL image 6 and returns a transformed version. E.g, ``transforms.RandomCrop`` 7 temporal_transform (callable, optional): A function/transform that takes in a list of frame indices 8 and returns a transformed version 9 target_transform (callable, optional): A function/transform that takes in the 10 target and transforms it. 11 loader (callable, optional): A function to load an video given its path and frame indices. 12 Attributes: 13 classes (list): List of the class names. 14 class_to_idx (dict): Dict with items (class_name, class_index). 15 imgs (list): List of (image path, class_index) tuples 16 """ 17 18 def __init__(self, 19 root_path, 20 annotation_path, 21 subset, 22 n_samples_for_each_video=1, 23 spatial_transform=None, 24 temporal_transform=None, 25 target_transform=None, 26 sample_duration=16, 27 get_loader=get_default_video_loader): 28 self.data, self.class_names = make_dataset( 29 root_path, annotation_path, subset, n_samples_for_each_video, 30 sample_duration) 31 32 self.spatial_transform = spatial_transform 33 self.temporal_transform = temporal_transform 34 self.target_transform = target_transform 35 self.loader = get_loader() 36 self.index = 0 37 38 def __getitem__(self, index): 39 """ 40 Args: 41 index (int): Index 42 Returns: 43 tuple: (image, target) where target is class_index of the target class. 44 """ 45 path = self.data[index]['video'] 46 47 frame_indices = self.data[index]['frame_indices'] 48 if self.temporal_transform is not None: 49 frame_indices = self.temporal_transform(frame_indices) 50 clip = self.loader(path, frame_indices) 51 if self.spatial_transform is not None: 52 self.spatial_transform.randomize_parameters() 53 clip = [self.spatial_transform(img) for img in clip] 54 print(clip) 55 clip = mindspore.ops.Stack(clip, 0).permute(1, 0, 2, 3) 56 57 target = self.data[index] 58 if self.target_transform is not None: 59 target = self.target_transform(target) 60 61 return clip, target 62 63 def __len__(self): 64 return len(self.data) 65 66 def __iter__(self): 67 self.index = 0 68 return self 69 70 def __next__(self): 71 if self.index > len(self.data): 72 raise StopIteration 73 else: 74 path = self.data[self.index]['video'] 75 76 frame_indices = self.data[self.index]['frame_indices'] 77 if self.temporal_transform is not None: 78 frame_indices = self.temporal_transform(frame_indices) 79 clip = self.loader(path, frame_indices) 80 if self.spatial_transform is not None: 81 self.spatial_transform.randomize_parameters() 82 clip = [self.spatial_transform(img) for img in clip] 83 print(clip) 84 clip = mindspore.ops.Stack(clip, 0).permute(1, 0, 2, 3) 85 86 target = self.data[self.index] 87 if self.target_transform is not None: 88 target = self.target_transform(target) 89 self.index += 1 90 return clip, target报错信息如下:原因分析:1. 用户脚本中的Kinetics类中第38行定义了__getitem__实现了随机访问,并且在第66,70行又定义了__iter__和__next__实现了迭代器的顺序访问,只需要选择其中任意一种即可。2. 脚本中第84行中使用了MindSpore计算侧的算子:mindspore.ops.Stack,因为GeneratorDataset进行数据加载时可使用多线程或多进程进程并发加载,而当前计算侧算子不能做到线程安全,可能导致不可预期的错误。解决方法:选择合适的数据输入:随机访问(__getitem__),顺序访问(__iter__,__next__)两者选其一即可。代码中第55,84行数据处理时使用numpy.stack来替换mindspore.ops.Stack。
上滑加载中
推荐直播
-
全面解析华为云EI-API服务:理论基础与实践应用指南
2024/11/29 周五 18:20-20:20
Alex 华为云学堂技术讲师
本期直播给大家带来的是理论与实践结合的华为云EI-API的服务介绍。从“主要功能,应用场景,实践案例,调用流程”四个维度来深入解析“语音交互API,文字识别API,自然语言处理API,图像识别API及图像搜索API”五大场景下API服务,同时结合实验,来加深开发者对API服务理解。
回顾中 -
企业员工、应届毕业生、在读研究生共探项目实践
2024/12/02 周一 19:00-21:00
姚圣伟 在职软件工程师 昇腾社区优秀开发者 华为云云享专家 HCDG天津地区发起人
大神带你一键了解和掌握LeakyReLU自定义算子在ONNX网络中应用和优化技巧,在线分享如何入门,以及在工作中如何结合实际项目进行学习
即将直播 -
昇腾云服务ModelArts深度解析:理论基础与实践应用指南
2024/12/03 周二 14:30-16:30
Alex 华为云学堂技术讲师
如何快速创建和部署模型,管理全周期AI工作流呢?本期直播聚焦华为昇腾云服务ModelArts一站式AI开发平台功能介绍,同时结合基于ModelArts 的实践性实验,帮助开发者从理论到实验更好地理解和使用ModelArts。
去报名
热门标签