一、案例环境描述

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等工具来定位狂打日志的进程,找出相应的日志文件,再通过应用程序接口

调整日志界别来解决问题

最新文章

  1. js 表单验证控制代码大全
  2. Glusterfs分布式存储介绍(一)
  3. iOS各个版本的新特性介绍
  4. Android是如何绘制View的
  5. Discuz 论坛的搭建(五)
  6. 关于1>LINK : fatal error LNK1168: 无法打开 ....exe或者....dll进行写入的问题
  7. Android中Chronometer 计时器和震动服务控件
  8. html标签总结。
  9. HDU 2544 最短路 SPFA 邻接表 模板
  10. underscore.js中的节流函数debounce及trottle
  11. pudian
  12. objective-c中类似ruby枚举类的实例方法
  13. EXCEL解析之终极方法WorkbookFactory
  14. net core swagger接口
  15. js动态改变setInterval的时间
  16. (转)并发编程 – Concurrent 用户指南
  17. 面试简单整理之Redis
  18. oc中的oop基础及类的基本介绍
  19. 【BZOJ2000】[HNOI2000]取石头游戏(贪心,博弈论)
  20. Linux 最小安装常用包

热门文章

  1. ICP 匹配定位算法学习记录
  2. python-7-数据结构与类型转换
  3. 搭建rsyslog日志服务器
  4. Oracle 查询练习
  5. Scrapy-Splash简介及验证码的处理(一)
  6. MySQL入门——在Windows下安装MySQL
  7. Java学习——日期类
  8. https申请证书并部署到网站流程,浏览器验证证书流程
  9. springmvc在使用@ModelAttribute注解获取Request和Response会产生线程并发不安全问题(转)
  10. echarts折线图上下颜色渐变样式