一、案例环境描述

1、环境准备

2CPU,4GB内存

预先安装docker sysstat工具

2、温馨提示

案例中 Python 应用的核心逻辑比较简单,你可能一眼就能看出问题,但实际生产环境中的源码就复杂多了。所以,
我依旧建议,操作之前别看源码,避免先入为主,要把它当成一个黑盒来分析。这样 你可以更好把握住,怎么从系统的资源使用问题出发,分析出瓶颈
所在的应用,以及瓶颈在应用中大概的位置

3、应用环境

1、运行目标应用

docker run -v /tmp:/tmp --name=app -itd feisky/logapp 

2、确认应用正常启动

ps -ef | grep /app.py
root 18940 18921 73 14:41 pts/0 00:00:02 python /app.py

二、故障现象

1、发现故障

# 按 1 切换到每个 CPU 的使用情况
$ top
top - 14:43:43 up 1 day, 1:39, 2 users, load average: 2.48, 1.09, 0.63
Tasks: 130 total, 2 running, 74 sleeping, 0 stopped, 0 zombie
%Cpu0 : 0.7 us, 6.0 sy, 0.0 ni, 0.7 id, 92.7 wa, 0.0 hi, 0.0 si, 0.0 st
%Cpu1 : 0.0 us, 0.3 sy, 0.0 ni, 92.3 id, 7.3 wa, 0.0 hi, 0.0 si, 0.0 st
KiB Mem : 8169308 total, 747684 free, 741336 used, 6680288 buff/cache
KiB Swap: 0 total, 0 free, 0 used. 7113124 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
18940 root 20 0 656108 355740 5236 R 6.3 4.4 0:12.56 python
1312 root 20 0 236532 24116 9648 S 0.3 0.3 9:29.80 python3

千万记得一定要用机械硬盘

2、性能现象

三、分析过程

1、iowait超过了90%分析

查看I/O使用情况

# -d 表示显示 I/O 性能指标,-x 表示显示扩展统计(即所有 I/O 指标)
$ iostat -x -d 1
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util
loop0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdb 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sda 0.00 64.00 0.00 32768.00 0.00 0.00 0.00 0.00 0.00 7270.44 1102.18 0.00 512.00 15.50 99.20

实际测试截图

观测结果分析

2、pidstat分析python进程

1、观察进程I/O使用情况

pidstat -d 1 

15:08:35      UID       PID   kB_rd/s   kB_wr/s kB_ccwr/s iodelay  Command
15:08:36 0 18940 0.00 45816.00 0.00 96 python 15:08:36 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
15:08:37 0 354 0.00 0.00 0.00 350 jbd2/sda1-8
15:08:37 0 18940 0.00 46000.00 0.00 96 python
15:08:37 0 20065 0.00 0.00 0.00 1503 kworker/u4:2

2、实际测试截图

3、pidstat结果观测分析

实际测试说明

1、io使用没有达到90%,而实际测试是14.9(最高)

2、每秒的写请求是64,而实际测试是85

3、写大小32MB,而实际测试是107MB

4、请求队列是1100而实际测试是23.93

3、strace分析python进程调用

1、strace查看指定PID调用情况

strace -p 18940
strace: Process 18940 attached
...
mmap(NULL, 314576896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0f7aee9000
mmap(NULL, 314576896, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f0f682e8000
write(3, "2018-12-05 15:23:01,709 - __main"..., 314572844
) = 314572844
munmap(0x7f0f682e8000, 314576896) = 0
write(3, "\n", 1) = 1
munmap(0x7f0f7aee9000, 314576896) = 0
close(3) = 0
stat("/tmp/logtest.txt.1", {st_mode=S_IFREG|0644, st_size=943718535, ...}) = 0

2、实际测试截图:机械硬盘 centos

3、strace结果观测分析

4、lsof查看python进程打开了那些文件

1、查看进程打开了那些文件

lsof -p 18940
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
python 18940 root cwd DIR 0,50 4096 1549389 /
python 18940 root rtd DIR 0,50 4096 1549389 /

python 18940 root 2u CHR 136,0 0t0 3 /dev/pts/0
python 18940 root 3w REG 8,1 117944320 303 /tmp/logtest.txt

2、实际测试截图

3、lsof输出观测分析

3、查看源代码

# 拷贝案例应用源代码到当前目录
$ docker cp app:/app.py . # 查看案例应用的源代码
$ cat app.py logger = logging.getLogger(__name__)
logger.setLevel(level=logging.INFO)
rHandler = RotatingFileHandler("/tmp/logtest.txt", maxBytes=1024 * 1024 * 1024, backupCount=1)
rHandler.setLevel(logging.INFO) def write_log(size):
'''Write logs to file'''
message = get_message(size)
while True:
logger.info(message)
time.sleep(0.1) if __name__ == '__main__':
msg_size = 300 * 1024 * 1024
write_log(msg_size)

  继续查看源码

def set_logging_info(signal_num, frame):
'''Set loging level to INFO when receives SIGUSR1'''
logger.setLevel(logging.INFO) def set_logging_warning(signal_num, frame):
'''Set loging level to WARNING when receives SIGUSR2'''
logger.setLevel(logging.WARNING) signal.signal(signal.SIGUSR1, set_logging_info)
signal.signal(signal.SIGUSR2, set_logging_warning)

分析这个源码,我们发现,它的日志路径是/tmp/logtest.txt、默认记录 INFO 级别以上的所有日志,而且每次写日志的大小是 300MB。这跟我们上面的分析结果是一致的

四、解决方案

1、运行kill命令给进程发SIGUSR2 信号

kill -SIGUSR2 18940 

2、再执行top和iostat观察

top

top
...
%Cpu(s): 0.3 us, 0.2 sy, 0.0 ni, 99.5 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 st

iowat

iostat -d -x 1
Device r/s w/s rkB/s wkB/s rrqm/s wrqm/s %rrqm %wrqm r_await w_await aqu-sz rareq-sz wareq-sz svctm %util
loop0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sdb 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00
sda 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00

到这里,我们不仅定位了狂打日志的应用程序,并通过调用高日志级别的方法,完美解决了I/O的性能瓶颈

五、小结

日志,是了解应用程序内部运行情况、最常用、也最高效的工具、无论是操作系统、还是应用程序、都会记录大量的运行日志,以便时候查看历史记录,

这些日志一般按照不同的级别来开启,比如,开发环境通常打开调式级别的日志,而线上环境则只记录警告和错误日志

在排查应用程序问题时,我们可以能需要,在线上环境临时开启应用程序的调试日志,有时候,事后一不小心忘了调回去,没把线上的日志调高到警告级别,

可能会导致cpu使用率、磁盘I/O等一系列的性能问题,严重时,甚至会影响同一台服务器上运行的其他应用程序

今后,在碰到这种“狂打日志”的场景时,你可以用iostat、strace、losf等工具来定位狂打日志的进程,找出相应的日志文件,再通过应用程序接口

调整日志界别来解决问题

Linux性能优化实战学习笔记:第二十六讲的更多相关文章

  1. Linux性能优化实战学习笔记:第六讲

    一.环境准备 1.安装软件包 终端1 机器配置:2 CPU,8GB 内存 预先安装 docker.sysstat.perf等工具 [root@luoahong ~]# docker -v Docker ...

  2. Linux性能优化实战学习笔记:第六讲1

    一.环境准备 1.安装软件包 终端1 机器配置:2 CPU,8GB 内存 预先安装 docker.sysstat.perf等工具 [root@luoahong ~]# docker -v Docker ...

  3. Linux性能优化实战学习笔记:第十七讲

    一.缓存命中率 1.引子 1.我们想利用缓存来提升程序的运行效率,应该怎么评估这个效果呢? 用衡量缓存好坏的指标 2.有没有哪个指标可以衡量缓存使用的好坏呢? 缓存命中率 3.什么是缓存命中率? 所谓 ...

  4. Linux性能优化实战学习笔记:第七讲

    一.进程的状态 1.命令查看 top PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 28961 root 20 0 43816 3148 ...

  5. Linux性能优化实战学习笔记:第四讲

    一.怎么查看系统上下文切换情况 通过前面学习我么你知道,过多的上下文切换,会把CPU时间消耗在寄存器.内核栈以及虚拟内存等数据的保存和回复上,缩短进程真正运行的时间,成了系统性能大幅下降的一个元凶 既 ...

  6. Linux性能优化实战学习笔记:第八讲

    一.环境准备 1.在第6节的基础上安装dstat wget http://mirror.centos.org/centos/7/os/x86_64/Packages/dstat-0.7.2-12.el ...

  7. Linux性能优化实战学习笔记:第十一讲

    一.性能指标 1.性能指标思维导图 2.CPU使用率 3.CPU平均负载 4.CPU缓存的命中率 CPU 在访问内存的时候,免不了要等待内存的响应.为了协调这两者巨大的性能差距,CPU 缓存(通常是多 ...

  8. Linux性能优化实战学习笔记:第四十五讲

    一.上节回顾 专栏更新至今,四大基础模块的最后一个模块——网络篇,我们就已经学完了.很开心你还没有掉队,仍然在积极学习思考和实践操作,热情地留言和互动.还有不少同学分享了在实际生产环境中,碰到各种性能 ...

  9. Linux性能优化实战学习笔记:第三十六讲

    一.上节总结回顾 上一节,我们回顾了经典的 C10K 和 C1000K 问题.简单回顾一下,C10K 是指如何单机同时处理 1 万个请求(并发连接 1 万)的问题,而 C1000K 则是单机支持处理 ...

  10. Linux性能优化实战学习笔记:第四十三讲

    一.上节回顾 上一节,我们了解了 NAT(网络地址转换)的原理,学会了如何排查 NAT 带来的性能问题,最后还总结了 NAT 性能优化的基本思路.我先带你简单回顾一下. NAT 基于 Linux 内核 ...

随机推荐

  1. idea2019最新注册码(亲测有效)

    序言 最近发现经常用的idea注册用的License Server 又不能用了,估计是被"约谈了".内容如下: 虽然Community版本是免费使用的,但是在使用的过程中会出现各种 ...

  2. 原创的离线版 Redis 教程,给力!

    嗯,你没看错,松哥又给大家送干货来了.这次是可以离线阅读的 PDF 版教程哦. 之前一直有小伙伴问我有没有 Redis 的电子书,老实说,有是有,但是公开给大家分享,其实有一点点风险,毕竟这都是有版权 ...

  3. 解决Git 克隆代码 The remote end hung up unexpectedly错误

    从GitHub上克隆一个项目一直不成功!猜想可能是文件太大超时或者网络太慢超时! 解决方案: 配置 git config -- git config -- 增加最低速时间,but,还是不行! 公司网络 ...

  4. 【JS】---4用JS获取地址栏参数方法

    用JS获取地址栏参数方法 // 方法一:采用正则表达式获取地址栏参数:( 强烈推荐,既实用又方便!) function GetQueryString(name) { var reg = new Reg ...

  5. 腾讯云-ASP.NET Core+Mysql+Jexus+CDN上云实践

    腾讯云-ASP.NET Core+Mysql+Jexus+CDN上云实践.md 开通腾讯云服务器和Mysql 知识点: ASP.NET Core和 Entity Framework Core的使用 L ...

  6. IIS创建文件服务器(WebDAV)

    1.安装IIS,选择安装WEBDAV组件.然后新建站点,站点目录不需要额外设置任何权限 安装完成后组件: 2.配置WebDAV: 添加创作规则:允许某用户写入,其他所有用户读取.(写入规则必须要放在第 ...

  7. JS运算符类型

    一.运算符类型 1.算术运算符: 用于各类数值运算,包括加(+).减(-).乘(*).除(/).求余(或称模运算,%).自增(++).自减(--)共七种. 2.关系运算符: 用于比较运算.包括大于(& ...

  8. js javascript map函数去重功能的使用实例

    js javascript map函数去重功能的使用实例 先上一个实战例子代码 var map = new Map(); for(var i=0; i<=9; i++){ map.set(i,i ...

  9. css中的行内元素和块级元素总结

    块级元素 <address>,  <button>,  <caption>,  <dd>,  <del>,  <div>,  & ...

  10. python类模拟电路实现

    实现电路: 实现方法: class LogicGate(object): def __init__(self, n): self.name = n self.output = None def get ...