说起运维, 大家可能想到的第一个词就是“苦逼”🤔。 但近些年来, 这个职位发生了翻天覆地的变化: 人肉运维(PE) → 自动化运维(DevOps) → 智能运维(AIOps)。 身为SRE 大军中的一员(什么是 SRE), 也在智能运维的边缘试探: 希望打造监控告警「智能降噪」, 「根因定位」, 「自愈」的处理流程, 终极目标就是让每个人都睡个好觉。
而上述流程中不是核心, 却不可或缺的一部分就是投递告警时, 将隐晦的告警消息(文字)可视化,转化为生动的图片与诊断结果。 由于我们的整个平台是由 Python 搭建的, 关于绘图调研过多个第三方工具, 但不是太慢就是依赖过重, 最终选择了经典的 Matplotlib.
问题:
处理告警是 I/O 密集型的场景(拉取数据等操作), 自然而然的开启了多线程提升处理效率. 但有一次发生故障, 并导致报警疯狂投递时, 发现了一件诡异的事情: 消息里的图片是全白的或者错位了 :(
重现:
根据多年的经验(pingganjue), 发现引入的 import matplotlib.pyplot as plt
是个全局变量.. 应该就是它引起的线程不安全.
写了个小 demo 重现了一下:
1 | import time |
如果 plot A
和 plot B
都是画一条线的话, 第三步保存的时候, 就会把画了两条线的图保存了.
解决:
在说解决方案前, 需要了解一个概念: 原子操作(atomic operation)
python 中很有趣的一点: sort
是原子操作, 而+=
不是原子操作。 详情可以阅读这篇文章, 写的很好: 《深入理解 GIL:如何写出高性能及线程安全的 Python 代码》
用 matplot 画图绝对不是个原子操作, 那要怎么解决线程的安全的问题呢? 要是觉得不安全, 就加个锁🔒呗
1 | import logging |
从 Output 可以看到, A&B 画图和保存的顺序没有错乱了。 当加锁会导致 deadlock 的情况, 这个 case 不太容易出现, 但还是要格外的小心。
其他:
分享一下我用 matplotlib 画的酷酷的图💪: