如何实现Django规模化:进阶教程

来源:互联网 发布:熊本熊mac电脑壁纸 编辑:程序博客网 时间:2024/06/06 15:41

提供:ZStack云计算

系列教程

本教程为Django规模化系列三篇中的第三篇。

导言

在之前的教程中,我们已经将Django部署在Droplet当中。当站点流量提升时,我们则可通过寻找瓶颈以确定问题并加以修复。然而,如果流量持续增长,我们该如何进一步解决问题?

本文主要面向Ubuntu 12.04系统,但大家完全可以将核心思路应用在其它Linux版本当中。

如果大家使用Apache,则推荐各位参阅webserver优化一文。如果大家使用Nginx,这些提示也同样适用。

将一切纳入缓存

如果大家创建了CMS以管理站点,那么应该能够实现比较积极的缓存策略。我个人倾向使用Varnish,这套前端缓存方案适用于Apache或Nginx。如果符合这一情况,推荐大家参阅Apache教程以及Nginx教程(是的,Nginx教程中探讨的是PHP,但也适用于Django)。

除非精心配置,否则Varnish的缺点在于其会将整套站点全部纳入缓存。举例来说,如果大家的站点中存在大量动态内容,那么Varnish的这种缓存策略可能引发错误。这意味着当A用户将某项货品加入购物车,B用户查看到的仍是缓存中的结果,意味着其能够将已经被A购买的货品同样加入购物车——这样显然会带来问题。

另一项弊端在于,Django在默认情况下会强制从缓存内容中读取上游缓存,这可能导致冲突并引发反常行为。

但好消息是,如今Django也拥有了自己的缓存框架,其拥有以下两项突出优势:

  1. 其能够提升应用程序的缓存友好度,从而更好地同Varnish相配合(如果大家选择使用Varnish)。
  2. 允许用户控制对哪些站点组成部分进行缓存处理。

如果大家使用的Droplet配置较低,则建议各位使用数据库缓存机制。其更易启用且不会带来额外的服务器进程。如果大家的内存量比较充裕,则可选择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顺利协作,则请参阅per-site缓存配置一文。不过这部分内容没什么新鲜,所以下面一起聊聊per-view缓存。

假设我们拥有一套购物车产品页面,其变更极少且动态程度不高。另外,假定我们计划对product_detail(请求product_id)进行缓存处理。

这时大家可以使用简单的decorator实现缓存:

from django.views.decorators.cache import cache_page@cache_page(60 * 60)def product_detail(request, product_id):...

其负责对60分钟内的视图进行缓存(60秒 x 60分钟)。

另外,我推荐大家参阅几项重要提示一文。其中一项重要功能在于根据标题头进行分类。如果大家的站点需要被翻译成多种语言,或者打算根据不同浏览器版本提供不同内容,这篇说明将很有帮助。

如何处理频繁变更的内容?

如果大家构建的是半动态站点,即应用需要不断接收频繁刷新的评论或数据,那么缓存机制的使用就很有说道了。

首先确定是否全部视图都能够进行主动缓存,而后再着手进行缓存。

其次,确定静态内容的容错时间以确保用户不会因缓存机制而访问到过期数据。例如,某站点的活跃评论容错能力在1到5分钟之间,那么缓存页面的时长也应设为同样水平。

大家可能想象不到——事实上仅仅10秒的缓存周期也能够起到良好的作用。在多数情况下,繁忙的站点往往会由于特定页面的访问量激增而面临过载。

如果有10000名用户在一分钟内访问站点中的某一页面,则意味着服务器需要将全部查询同时发送至数据库。这时,如果我们将缓存时长设定为10秒,则意味着该页面的每分钟查询次数仅为6次——无论并发访问强度有多大。如此一来,任何服务器都能轻松应对这样的流量。

电子商务站点往往需要谨慎地处理会话数据。这时大家应当考虑标题头分类选项是否适用,毕竟创新才是实现成功的最佳途径!

站点往往会利用Ajax支持全部HTML页面中的静态内容并载入per-user会话信息。立足于电商站点,用户通常不会注意到其购物车需要5秒钟才能载入完成——毕竟当前页面还有很多其它内容供他们浏览。

提供静态内容

Django的内置开发服务器拥有出色的静态内容提供能力。大多数Django站点都会利用其提供静态内容。

但事实上,除非需要控制对内容的访问,否则大家应该尽可能尝试其它方式。

  • 动态生成CSS - 这种情况下请使用Grunt或者Less 在部署前构建CSS。如果需要在服务器上周期性生成CSS,则可使用后台任务来实现。
  • 验证用户访问 - 是的,这种情况适合使用Django。
  • 其它情况 - 我们应当配置静态内容以将其托管在Web服务器之上。

这里推荐大家参阅静态文件说明文档。如果大家懒得查看,我将其中的要点总结如下:

在本地开发设备上,向“settings.py”文件添加以下行:

STATIC_ROOT = os.path.join(BASE_DIR, "static")

而后运行manage.py命令:

python manage.py collectstatic

现在大家会在项目root目录中发现一个名为“static”的文件夹,其中包含大量静态文件,例如CSS与JavaScript。特别是在已经启用了admin app的情况下。

要使用这些文件,大家需要配置Django以确保其了解在哪里找到这些文件。在settings.py文件夹中,我们需要进行以下配置:

STATICFILES_DIRS = (os.path.join(BASE_DIR, "static"),)

现在如果大家运行开发者服务器并查看admin区域内的任意页面源,则会看到来自/static/admin/css/的CSS文件。

现在,在部署应用程序时,我们可以通过将Apache或Nginx直接指向这些文件来确保其交付以/static/开头的各文件。

如果大家的应用部署在/src/myapp/,则会生成一个名为/src/myapp/static/admin/css/base.css的文件,因此以如下方式配置Web服务器:

Apache:Alias /static/ "/srv/myapp/static/"Nginx:server {...location /static/ {    alias /srv/myapp/static/;}}

在这两种情况下,大家可以将/static/ URL指向磁盘驱动器上应对的文件位置。这样,Apache与Nginx就能够以等同于Django的速度提供这些内容。

使用uWSGI

如果希望快速发布一套站点或者实现最佳性能,我会使用Apache的mod_wsgi。

uWSGI属于一项服务器进程,其能够在Web服务器之外运行我们的应用程序。在使用Apache时,大家需要安装以下软件包:

sudo apt-get install uwsgi uwsgi-plugin-python uwsgi-plugin-cgi

如果大家利用Apache作为Web服务器,则需要安装:

sudo apt-get install libapache2-mod-uwsgi

完成之后,大家会注意到两个用于配置的新文件夹:

/etc/uwsgi/apps-available与/etc/uwsgi/apps-enabled,其与虚拟主机上的Apache或Nginx配置相同。

如果需要创建一个新应用,大家可以向/etc/uwsgi/apps-available中添加一个新的配置文件,而后创建一条由apps-enabled指向它的symlink。

假设大家创建了一个名为newproject的新项目,并将其保存在/srv/newproject位置处。由于其属于典型的Django项目,/srv/newproject/newproject/下将生成一个settings.py文件。

创建一个名为`/etc/uwsgi/apps-available/newproject.ini的新文件,并将其内容编辑为:

[uwsgi]touch-reload = /tmp/newprojectsocket = 127.0.0.1:3031workers = 2chdir = /srv/newprojectenv = DJANGO_SETTINGS_MODULE=newproject.settingsmodule = django.core.handlers.wsgi:WSGIHander()

现在创建一个名为/tmp/newproject的空文件:

touch /tmp/newproject

现在通过将该文件链至apps-enabled实现应用激活:

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以了解运行及错误信息。

Apache配置

如果大家正在使用mod_wsgi,到这里请将其禁用并启用mod_uwsgi。这项操作可能造成错误,因此确保首先在测试服务器上尝试,直到找到正确的配置方式。

sudo a2dismod wsgisudo a2enmod uwsgi

现在对应用进行配置更新。在apache config中应该存在以下行:

WSGIScriptAlias / /srv/newproject/newproject.wsgi

将其更新为:

SetHandler uwsgi-handleruWSGISocket 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的重要优势之一就是允许我们限定应用程序在内存中的运行次数。如果大家前面提到的配置,则内存中通常存在1个父进程与2个worker。如果大家需要更多worker,则可在.ini文件中进行编辑以增加“workers”的数量。而后使用“sudo service uwsgi reload”命令以重载uWSGI。

在增加进程数量之前,请考虑可供使用的服务器内存是否充足。相较于mod_wsgi,使用mod_uwsgi能够降低内存使用量。另外请关注uWSGI worker的大小,其在应用使用过程中可能出现体积膨胀。这种情况与内存泄露无关,单纯是因为内存内数据集过小。

最大程度提升利用率

在应用程序运行一段时间之后,检查服务器的内存使用量。

使用free -m命令以查看缓冲区/缓存与应用所使用的实际内存量。其中的关键值在于“free”列顶端的数字。在理想情况下,高强度服务器几乎会用掉全部内存。

如果大家发现仍有内存余量但站点速度开始变慢,则应深入检查是否存在其它问题。

一般来讲,大家会发现进程在等待MySQL的响应。这意味着我们需要为数据库分配更多系统内存。这种情况在默认设置下较为常见,因为MySQL会采取较为保守的内存分配策略。

另外,也有可能是分配给磁盘缓存的内存容量太低。是否存在不必要的进程在占用内存?在默认情况下,Linux会尽可能为磁盘缓存分配更多内存,并在应用需要内存时及时将其释放。

在理想状态下,VPS环境应该永远不会出现内存限制,所有只读资产全部存在于缓存当中。因此,请尽量去掉不必要的后台进程。

总结

到这里,大家应该已经了解了与缓存相关的各类知识,包括利用Varnish进行全站缓存以及立足于per-view将设置纳入缓存。

另外,也需要确保静态文件由Web服务器提供——而非Django。

大家也掌握了如何利用uWSGI将Djngo应用运行在Web服务器进程之外,从而实现性能提升。(在这里,推荐大家参阅如何实现卓越Apache/uWSGI一文。)

最后,我们探讨了如何将尽可能多的内存分配给服务器应用以提高资源利用率。

本文来源自DigitalOcean Community。英文原文:How to Scale Django: Beyond the Basics By Matthew Nuzum

翻译:diradw

0 0