如何扩展Django:超越基础
入门
你已经将 Django 部署到你的 Droplet 并且生活很好。 随着网站流量的增长,您遇到了一些性能问题,但您 找到了瓶颈 并修复了它。 但是,您网站的流量不断增长。 不知何故,您需要更高的性能……您能做什么?
让我们深入了解一下我们的应用程序和服务器配置。 本文假设您使用的是 Ubuntu 12.04,但其原理适用于任何版本的 Linux。
如果您使用的是 Apache,那么您应该按照 优化您的网络服务器 的说明进行操作。 如果您使用的是 Nginx,这些技巧也适用于您。
缓存一切
如果您创建了一个 CMS 来管理您的站点,那么您可能可以容忍非常激进的缓存。 在这种情况下,我最喜欢的工具是 Varnish,它是一个位于 Apache 或 Nginx 前面的前端缓存。 在这种情况下,请查看现有的 Apache 教程和 Nginx 的 教程(是的,Nginx 文章讨论了 PHP,但它也适用于 Django)。
缺点是 Varnish,除非你仔细配置,否则会缓存你的整个站点。 如果您有动态内容,例如如果您的网站更像是一个应用程序,Varnish 可能会带来很多麻烦。 假设用户 A 访问该站点并将商品添加到他们的购物车中。 然后用户 B 访问该站点并看到购物车的缓存版本,其中包含用户 A 的商品。 不好。
另一个缺点是,默认情况下,Django 会尝试强制上游缓存缓存内容,这会导致一些以不稳定行为结束的战斗。
但我有个好消息:Django 有它自己的 缓存框架 ,它会给你两件事。
- 它将使您的应用程序对缓存更加友好,以便与 Varnish 配合使用(如果您选择使用它)
- 它可以让您控制网站的哪些部分被缓存
如果您使用的是较小的液滴,我建议您使用数据库缓存。 它很容易打开并避免需要运行另一个服务器进程。 如果你有足够的 RAM memcached 提供了一个优秀的缓存后端。 本文将演示基于数据库的缓存。
仔细阅读上面链接的文档以获取许多出色的细节,但这是您开始时需要做的事情。
为缓存创建一个数据库表。 在这种情况下,我们的表称为“cache_table”:
python manage.py createcachetable cache_table
现在编辑您的 settings.py 文件并通过添加以下行来配置缓存:
CACHES = { 'default': { 'BACKEND': 'django.core.cache.backends.db.DatabaseCache', 'LOCATION': 'cache_table', } }
您可以配置其他配置选项。 在 文档 中了解它们。 我会特别检查 TIMEOUT 和 CULL_FREQUENCY。
现在你需要决定缓存什么。 如果您只是想让您的站点与 Varnish 一起工作得更好,请查看 每站点缓存配置 。 然而,这并不是很令人兴奋,所以让我们深入研究按视图缓存。
假设您有一个购物车产品页面,该页面很少更改并且不是很动态。 您有一个名为 product_detail(request, product_id)
的视图,它非常适合缓存。
您可以使用简单的装饰器为此启用缓存:
from django.views.decorators.cache import cache_page @cache_page(60 * 60) def product_detail(request, product_id): ...
这将允许该视图缓存 60 分钟(60 秒 * 60 分钟)。
同样,我想参考 文档以获取一些出色的技巧 。 一项特别强大的功能是 根据标头 变化。 如果您有一个被翻译成多种语言的网站,或者您根据浏览器版本显示不同的内容,这会很有帮助。
经常变化的内容呢?
如果您有一个半动态站点,例如您的应用程序收到大量评论或数据频繁刷新,您可能会担心使用缓存。 这是一个合理的担忧。
首先确定所有可以积极缓存的视图,然后将它们缓存起来。
其次,确定您可以容忍某人看到陈旧内容的延迟多长时间。 例如,具有活跃评论的站点可能会容忍 1 到 5 分钟的延迟。 尽可能多地缓存页面。
你会感到惊讶——即使是 10 秒的缓存超时也会很有帮助。 在许多情况下,突然超载的繁忙站点通常会受到特定页面的流量冲击。
如果一分钟内有 10,000 人访问您网站上的某个页面,那么当对数据库的所有这些查询同时运行时,它将会融化您的服务器。 如果您将缓存设置为 10 秒,那么这意味着页面将每分钟仅重新生成 6 次,无论有多少人请求它。 任何服务器都可以做到!
电子商务网站和使用会话数据的网站必须非常小心。 调查 vary 标头选项以查看缓存是否可以在您的情况下工作。 不要害怕有创意!
站点有时会将所有 HTML 页面作为静态内容提供,并使用 Ajax 加载每个用户的会话信息。 对于电子商务网站,用户通常不会注意到他们的购物车计数是否需要 5 秒才能加载,只要他们可以正常浏览页面的其余部分。
提供静态内容
Django 的内置开发服务器很乐意为静态内容提供服务。 通常,我看到 Django 站点配置为生产使用,其中包含由 Django 提供的静态内容。
唯一应该这样做是如果您需要控制对内容的访问。 但即便如此,也要考虑其他方式。
- 动态生成的 CSS - 不,使用 Grunt 或 Less 之类的构建工具在部署之前构建您的 CSS。 如果需要定期在服务器上生成,请使用 后台任务 。
- Authenticated user access - 是的,您可能需要为此使用 Django。
- 其他一切 - 不,让我们使用您的网络服务器配置静态内容托管。
在我得到它之前,我必须阅读静态文件 的 文档几次。 我认为您应该自己通读一遍,但是如果您也遇到困难,可以使用以下场景:
在本地开发机器上,在“settings.py”中添加如下一行:
STATIC_ROOT = os.path.join(BASE_DIR, "static")
然后像这样运行 manage.py 命令:
python manage.py collectstatic
您现在应该在项目根目录中看到一个名为“static”的文件夹,在其中您将看到很多静态文件,例如 CSS 和 Javascript。 特别是如果您启用了管理应用程序。
要使用这些文件,您需要配置 Django 以便它知道在哪里查找它们。 在您的 settings.py 文件夹中,您需要这样的配置选项:
STATICFILES_DIRS = ( os.path.join(BASE_DIR, "static"), )
现在,如果您运行开发人员服务器并访问管理区域并查看任何页面的源代码,您应该会看到 CSS 文件是从 /static/admin/css/
提供的。
当您部署应用程序时,您现在可以通过将 Apache 或 Nginx 直接指向文件来为以 /static/ 开头的文件提供服务。
如果您的应用程序部署到 /srv/myapp/ 并且因此您有一个名为 /srv/myapp/static/admin/css/base.css
的文件,那么您可以像这样配置您的网络服务器:
阿帕奇:
Alias /static/ "/srv/myapp/static/"
Nginx:
server { ... location /static/ { alias /srv/myapp/static/; } }
在这两种情况下,您只需将 /static/ URL 指向硬盘驱动器上找到文件的确切位置。 Apache 和 Nginx 可以比 Django 更快地提供这些服务。
使用 uWSGI
如果我想快速发布一个站点,我使用 Apache 的 mod_wsgi。 如果我想要最好的性能,我会使用 uWSGI。 如果你想知道如何发音,那就是“你威士忌”。
uWSGI 是一个服务器进程,它在网络服务器之外运行您的应用程序。 对于 Apache,您需要安装以下软件包:
sudo apt-get install uwsgi uwsgi-plugin-python uwsgi-plugin-cgi
如果您使用 Apache 作为您的网络服务器,还需要安装:
sudo apt-get install libapache2-mod-uwsgi
完成后,您会注意到您现在有两个新的配置文件夹:/etc/uwsgi/apps-available
和 /etc/uwsgi/apps-enabled
,就像您在使用 Apache 或 Nginx 的虚拟主机配置中看到的一样.
当你想创建一个新的应用程序时,你将一个配置文件放在 /etc/uwsgi/apps-available
中,然后从应用程序启用创建一个符号链接。
假设您创建了一个名为 newproject 的项目,并将其放在 /srv/newproject
中。 与 Django 项目一样,在 /srv/newproject/newproject/settings.py
处有一个 settings.py 文件。
创建一个名为 `/etc/uwsgi/apps-available/newproject.ini 的文件并将其编辑为如下所示:
[uwsgi] touch-reload = /tmp/newproject socket = 127.0.0.1:3031 workers = 2 chdir = /srv/newproject env = DJANGO_SETTINGS_MODULE=newproject.settings module = django.core.handlers.wsgi:WSGIHander()
现在创建一个名为 /tmp/newproject
的空文件:
touch /tmp/newproject
您必须通过将文件链接到启用应用程序来激活应用程序:
sudo ln -s /etc/uwsgi/apps-available/newproject.ini /etc/uwsgi/apps-enabled/newproject.ini
然后重启uWSGI:
sudo service uwsgi restart
如果您检查“ps ax”的输出,您应该会看到一些与您的应用程序相关的进程正在运行。
请注意,如果您在同一台服务器上运行多个应用程序,那么您需要为每个应用程序的套接字使用不同的端口。
此外,如果你更新了你的应用程序,你应该简单地“触摸”文件 /tmp/newproject 并且 uWSGI 将重新加载自己。
您可以随时查看 /var/log/uwsgi/app/newproject.log
以查看信息消息和错误。
阿帕奇配置
如果你现在使用 mod_wsgi 是关键时刻。 您应该禁用 mod_wsgi 并启用 mod_uwsgi。 这可能会导致错误,因此请在测试服务器上执行此操作,直到找到正确的配置。
sudo a2dismod wsgi sudo a2enmod uwsgi
现在更新您的应用程序的配置。 在您的 apache 配置中,您可能有这样的一行:
WSGIScriptAlias / /srv/newproject/newproject.wsgi
你会更新成这样:
SetHandler uwsgi-handler uWSGISocket 127.0.0.1:3031
现在重新加载您的 Apache 配置:
sudo service apache2 reload
Nginx 配置
在您的服务器配置中,您需要将现有的 wsgi 指令替换为:
uwsgi_pass 127.0.0.1:3031; include uwsgi_params; uwsgi_param UWSGI_SCHEME $scheme;
很简单!
uWSGI 的好处
使用 uWSGI 的优点之一是您可以限制应用程序在内存中运行的次数。 通常会有一个父进程,如果您使用上面共享的配置,则有 2 个工作人员。 如果您需要更多工人,您可以简单地编辑应用程序的 .ini 文件并增加“工人”的数量。 然后使用“sudo service uwsgi reload”重新加载 uWSGI 以更新更改。
在增加进程数量之前,请考虑您正在使用多少服务器内存。 如果您在运行 mod_wsgi 之前应该会看到内存使用量减少。 请注意 uWSGI 工作人员的大小。 随着应用程序的使用,它们的大小往往会增加。 不是因为内存泄漏,而是因为内存中的数据集很小。
最大化利用率
在您的应用程序预热并使用了一段时间后,检查您的服务器的内存利用率。
使用命令 free -m
可以查看缓冲区/缓存使用了多少内存,而不是使用了多少内存。 应用程序。 键值是“免费”列中的顶部数字。 理想情况下,繁忙的服务器将使用几乎所有的 RAM。
如果您发现您有空闲的 RAM 并且您的网站速度很慢,那么请深入了解未充分利用的内容。
您经常会看到进程正在等待 MySQL。 可能是您需要为数据库分配更多系统内存。 这是非常常见的,因为 MySQL 的默认配置非常谨慎地使用 RAM。
也可能没有足够的 RAM 分配给磁盘缓存。 您是否有不需要的进程占用内存? 默认情况下,Linux 会为磁盘缓存分配尽可能多的 RAM,并在应用程序需要时不情愿地放弃它。
在内存受限的 VPS 环境中永远不会发生的理想情况下,所有只读资产都将缓存在缓存中。 因此摆脱不需要的后台进程。
包起来
您已经学习了一些缓存技术,包括使用 Varnish 缓存整个站点或基于每个视图控制缓存设置。
您已确保静态文件是由网络服务器而不是 Django 提供的。
你已经学习了如何使用 uWSGI 来提高你的 Django 应用程序的性能,方法是在网络服务器的进程外运行它。 (顺便说一句,感谢 Ricardo Pascal 的 出色的 Apache/uWSGI 文档)
最后,您了解了如何为服务器应用程序分配尽可能多的内存以最大限度地提高利用率。