“古怪的 Python 内存泄漏”怎么破?

笔者曾经开发过的几个大型 Django 应用程序都在某个时候出现了内存泄漏。Python 进程缓慢地增加它们的内存消耗,直到崩溃。这一点也不好玩。即使自动重新启动进程之后,仍然会有一些宕机问题。

Python 中的内存泄漏通常发生在无限增长的模块级变量中。这可能是一个具有无穷大 maxsize 的 lru_cache 变量,也可能是一个在错误范围内声明的简单列表。

泄漏也不是只有发生在你自己写的代码中才会影响你。例如,看看 BuzzFeed 的 Peter Karp 写的这篇优秀的文章(https://docs.python.org/3/library/tracemalloc.html),他在 Python 的标准库中发现了一个内存泄漏(已经修复了!)

解决方法

下面的解决方法都会在执行了很多请求或任务之后重新启动 worker 进程。这是一个清除任何潜在的无限积累的 Python 对象的简单方法。如果您的 web 服务器、队列 worker 或类似的应用程序有此能力,但还没有被功能化,请告诉我,我会添加它!

即使您现在还没有看到任何内存泄漏,添加这些解决方法也会提高您的应用程序的弹性。

Gunicorn

如果您正在使用 Gunicorn 作为您的 Python web 服务器,您可以使用--max-requests 设置来定期重启 worker。与它的兄弟--max-requests-jitter 配合使用以防止所有 worker 同时重启。这有助于减少 worker 的启动负载。

例如,在最近的一个项目中,我将 Gunicorn 配置为:

gunicorn --max-requests 1000 --max-requests-jitter 50 ... app.wsgi

对于此项目的流量水平、worker 数量和服务器数量来说,这将大约每1.5小时重新启动 worker。5% 的浮动足以消除重启负载的相关性。

Uwsgi

如果您正在使用 uwsgi,你可以使用它类似的 max-requests 设置。此设置在很多次请求之后,也会重新启动 worker。

例如,在以前的一个项目中,笔者在 uwsgi.ini 文件中像这样使用了这个设置:

[uwsgi]
master = true
module = app.wsgi
...
max-requests = 500

Uwsgi 还提供了 max-requests-delta 选项用于添加其他浮动。但由于它是一个绝对数字,所以配置起来要比 Gunicorn 更麻烦。如果你更改了 worker 的数量或 max-requests 的值,那你就需要重新计算 max-requests-delta 来保持您的浮动在一个特定的百分比。

Celery

Celery 为内存泄漏提供了几个不同的设置。

首先是 worker_max_tasks_per_child 设置。这将在 worker 子进程处理了许多任务之后重新启动它们。此设置没有浮动选项,但是 Celery 任务的运行时间范围很广,所以会有一些自然的浮动。

例如:

app = Celery("inventev")
app.conf.worker_max_tasks_per_child = 100

或者你正在使用 Django 设置:

CELERY_WORKER_MAX_TASKS_PER_CHILD = 100

100个任务比笔者上面建议的 web 请求数要小一些。在过去,笔者最终为 Celery 使用了较小的值,因为笔者在后台任务中看到了更多的内存消耗。(我想我还在 Celery 本身中遇到了内存泄漏。)

你可以使用的另一个设置是 worker_max_memory_per_child。这指定子进程在父进程替换它之前可以使用的最大千字节内存。它有点复杂,所以我还没用过。

如果你确实使用了 worker_max_memory_per_child 设置,那么你可能应该将其计算为总内存的百分比,并除以每个子进程。这样,如果你更改了子进程的数量或你的服务器的可用内存,它将会自动扩展。例如(未测试):

import psutil
celery_max_mem_kilobytes = (psutil.virtual_memory().total * 0.75) / 1024
app.conf.worker_max_memory_per_child = int(celery_max_mem_kilobytes / app.conf.worker_concurrency)

这使用psutil 来查找整个系统内存。它将最多75%(0.75)的内存分配给Celery,只有在服务器是一个专用 Celery 服务器的情况下你才会需要它。

跟踪泄漏

在 Python 中调试内存泄漏是很不容易的,因为任何函数都可以在任何模块中分配全局对象。它们也可能出现在与 C API 集成的扩展代码中。

笔者用过的一些工具:

  • 标准库模块 tracemalloc.

  • objgraph 和 guppy3 包都是在 tracemalloc 之前完成的,并尝试做类似的事情。它们都不太友好,但我以前成功地使用过它们。

  • Scout APM 用 CPython 的内存分配计数来检测每个“span”(请求、SQL 查询、模板标签,等等)。少数 APM 解决方案能做到这一点。提示:我维护着 Python 集成。

更新 (2019-09-19): Riccardo Magliocchetti在Twitter上提到,pyuwsgimemhog 项目可以解析 uwsgi 日志文件,并告诉您哪些路径正在泄漏内存。很简洁!

其他一些有用的博文:

结束啦!

祝您内存泄漏更少!

—Adam

英文原文:https://adamj.eu/tech/2019/09/19/working-around-memory-leaks-in-your-django-app/
好消息!51reboot python运维自动化课程10.13日,51reboot k8s课程10月份 。关注公众号;51reboot运维开发

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351

推荐阅读更多精彩内容

  • # Python 资源大全中文版 我想很多程序员应该记得 GitHub 上有一个 Awesome - XXX 系列...
    小迈克阅读 2,972评论 1 3
  • 分享一篇文章,总结了很多Python Web 部署的内容,原文位于:Python Web部署方式总结 不要让服务器...
    妄心xyx阅读 821评论 0 13
  • TP架构的概述:1、Think PHP使用了MVC模式 7、操作(方法) 8、模型(数据模型) 9、视图(页面) ...
    大菜鸟呀阅读 307评论 0 0
  • 现在是凌晨2:56,我在黑暗中面对着发光的屏幕,没有睡意,突然不明白自己难过从何而来,焦虑从何而来,明明,一切很好...
    指掌阅读 255评论 0 0
  • 本能是人类演化长河中给我们遗留下来的财富。 但是随着时间的推移,社会的进步有一些本能在这个时代里面可能就不合适了。...
    李菁_126班9509阅读 201评论 0 1