如何记录每个请求的 Django 应用程序的内存使用情况
Posted
技术标签:
【中文标题】如何记录每个请求的 Django 应用程序的内存使用情况【英文标题】:How to log memory usage of an Django app per request 【发布时间】:2012-08-28 06:24:33 【问题描述】:您知道记录 django 应用程序每个请求的内存使用情况的有效方法吗?
我有一个 apache/mod_wsgi/django 堆栈,它通常运行良好,但有时一个进程最终会占用大量内存。服务器最终内存不足,交换了很多,并且服务速度大大降低。
这种情况很难解决,因为我不知道该行为应该归咎于哪个请求,我无法重现它。
我想在生产环境中部署一些东西,以最小的开销记录进程在每次请求之前和之后的内存使用情况。
在我开始重新发明***之前,我的 djangoists 社区是否知道任何现有的解决方案来解决这个问题? 建议、中间件、sn-p 或 apache 日志配置值得赞赏。
(我认为)我不需要的是:
一组开发阶段分析/调试工具,我已经知道一些,如果我知道要分析/调试什么,我会使用它们,它看起来有点太多了,永远监控生产中运行的服务。最重要的是,这些 tol 通常显示的是代码碎片的 mem 使用报告,这将有助于查明错误的请求。 关于如何优化 django 应用程序的内存使用的一般建议,读起来总是很好,但这里的想法是“如何有效地跟踪需要优化的请求”。我最接近的搜索结果:
Django / WSGI - How to profile partial request? My profiling tools are per-request but app runs out of memory before then Django memory usage going up with every request Average php memory usage per request?【问题讨论】:
也许这个 modwsgi 选项 'maximum-requests=nnn' 会有所帮助。 "定义一个守护进程在关闭和重新启动之前应该处理的请求数限制。" @freestyler:是的,我已经在使用它了,但它有点没抓住重点。这个想法是提出轻微的错误请求以实际修复它们,而不是定期清理系统(这也可能有用)。此外,内存消耗请求可能会在重启后提前出现,这里没有相关性。 【参考方案1】:用于跟踪内存使用情况并立即生成可用结果的 Django 中间件需要挂钩进程请求和进程响应。换句话说,查看请求的开始和结束之间的差异,如果超过某个阈值,则记录警告。
一个完整的中间件示例是:
import os
import psutil
import sys
THRESHOLD = 2*1024*1024
class MemoryUsageMiddleware(object):
def process_request(self, request):
request._mem = psutil.Process(os.getpid()).memory_info()
def process_response(self, request, response):
mem = psutil.Process(os.getpid()).memory_info()
diff = mem.rss - request._mem.rss
if diff > THRESHOLD:
print >> sys.stderr, 'MEMORY USAGE %r' % ((diff, request.path),)
return response
这需要安装“psutil”模块来进行内存计算。
是蛮力,可能导致多线程系统中的误报。由于延迟加载,您还会看到它在加载内容时针对新进程的前几个请求触发。
【讨论】:
很好,谢谢你的回答!误报并不是真正的问题,因为其目的只是触发和集中进一步调查,将此信息与 PID 和日期/时间相结合可能有助于快速了解和消除这些情况。附带说明一下,可能值得将其放入 WSGI 中间件中,这样它就不会与 django 机器绑定。 作为一个 WSGI 中间件正确地完成它变得非常复杂。当我这样做时,它是针对 Django 用户的,所以作为 Django 中间件更容易做到这一点。 :-) 我收到此错误:“WSGIRequest”对象在此行没有属性“_mem”:diff = mem.rss - request._mem.rss @vijayshanker,我知道已经很久了,您可能已经发现了问题,但是,为了记录,您看到该错误的原因是某些请求不会同时触发 process_request和 process_response,例如 301(重定向),它只触发 process_response。这也意味着该解决方案存在缺陷。你至少应该在访问它之前检查请求是否有'_mem'。 一个非常有用的答案,但要小心,因为不会总是为相应的 process_response 调用调用 process_request(请参阅docs.djangoproject.com/en/1.8/topics/http/middleware/…)。我只是将 process_response 逻辑包装在“if hasattr(request, "_mem"):" 中并为我修复了它。【参考方案2】:这可能无法完全涵盖您的问题,但我建议尝试使用 nginx+uwsgi 而不是 apache2+mod_wsgi。在我的测试中,它变得更加稳定(mod_wsgi 在某些时候完全阻塞)、速度更快并且使用的内存更少(它可能完全解决了您的所有问题)。
关于跟踪内存使用情况,可以创建一个简单的中间件:
class SaveMemoryUsageMiddleware(object):
def process_response(self, request, response):
# track memory usage here and append to file or db
return response
并将其添加到您的中间件中。
对于内存跟踪代码,我建议查看: Total memory used by Python process?
但是,如果您可以避免在生产环境中执行此操作,可能会更好。仅用于开发和测试以追踪真正的问题。
【讨论】:
感谢您的回答,更换 apache 是有计划的,但优先级较低。 Nginx 已经在我的堆栈顶部。如果没有人想出现有的解决方案,中间件方式是我计划做的事情。感谢您提供有关python内部内存消耗监控的指针,这可能会有所帮助。 如果你的 Apache 使用了更多的内存,那么你的 Apache 设置错误,就这么简单。观看我的 PyCon 演讲,因为它涵盖了人们使用 Apache 默认值时常犯的错误。 lanyrd.com/2012/pycon/spcdg nginx 和 apache2 内部的工作方式有很大的不同。您不能期望基于进程/线程的池 (apache) 将使用与基于事件的池 (nginx) 相似的内存量。就这么简单。 我使用 Apache 和 Nginx 来托管 Django,在性能方面并没有太大区别。我见过的大多数基准测试也证实了这一点。以上是关于如何记录每个请求的 Django 应用程序的内存使用情况的主要内容,如果未能解决你的问题,请参考以下文章