解决Django应用程序中的内存泄漏问题

wangyibo00 2019-10-06

解决Django应用程序中的内存泄漏问题

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

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

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

解决方法

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

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

Gunicorn

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

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

解决Django应用程序中的内存泄漏问题

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

Uwsgi

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

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

解决Django应用程序中的内存泄漏问题

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

Celery

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

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

例如:

解决Django应用程序中的内存泄漏问题

或者您正在使用Django设置:

解决Django应用程序中的内存泄漏问题

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

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

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

解决Django应用程序中的内存泄漏问题

这使用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日志文件,并告诉您哪些路径正在泄漏内存。很简洁!

其他一些有用的博文:

  • Buzzfeed Tech撰写了一篇《如何在生产Python web服务上使用tracemalloc的指南》。
  • Fugue的文章也使用了tracemalloc。
  • 在Benoit Bernard的“古怪的Python内存泄漏”帖子中,他使用各种工具来跟踪C层面的泄漏。

结束语

祝您内存泄漏更少!

—Adam

英文原文:https://adamj.eu/tech/2019/09/19/working-around-memory-leaks-in-your-django-app/

译者:野生大熊猫

相关推荐

inspuryhq / 0评论 2020-07-28