部署Django

来源:互联网 发布:淘宝卖家在哪里登录 编辑:程序博客网 时间:2024/06/05 18:25

Django一开始,就有另一个重要的目标:Django应该容易被部署,并且它应该能够用有限的资源提供大量的服务。

这样的动机是很明显的,当你看到 Django的背景:堪萨斯州一个小巧的、家族式报纸企业负担不起高品质的服务器硬件,所以 Django的最初开发者们都非常的关心如何才能从有限的资源中挤压出最好的性能。确实,这些年来 Django的开发者们充当了他们自己的系统管理员。虽然他们的站点每天处理上千万的点击量,但他们确实没有那么多数量的硬件以至于*需要*专门的系统管理员。

Django成为一个开源项目后,关注在其性能和开发的简易性因为某些原因变得特别重要:业余爱好者也有同样的需求。有一些人想要花费仅仅 10美元并使用 Django 来体验制作一个中小规模流量的站点。

但是小规模的应用只是目标的一半而已。Django也需要能够增加其规模来满足大型公司和集团的需求。这里,Django采取了类似于 LAMP 形式的 Web 集的哲学理论,通常称为无共享(shared nothing)

什么是 LAMP

LAMP 这个缩写最初是创造它来描述一系列驱动 Web站点的流行的开源软件:

§ Linux(操作系统)

§ ApacheWeb服务器)

§ MySQL(数据库)

§ PHP(编程语言)

随着时间的推移,这个缩写已经变得涉及了更多开源软件栈的哲学思想,而不仅仅再是局限于特定的某一种了。所以当 Django使用 Python并可以使用多种数据库产品时,LAMP软件栈证实的一些理论就渗透到 Django中了。

这里尝试建立一个类似的缩写来描述 Django的技术栈。本书的作者喜欢用 LAPDLinux, Apache, PostgreSQL, and Django)或PAIDPostgreSQL, Apache, Internet, and Django)。使用 Django并采用 PAID吧!

无共享

无共享哲学的核心实际上就是应用程序在整个软件栈上的松耦合思想。这个架构的产生直接响应了当时的主流架构背景:将web应用服务器作为不可分的整体,它将语言、数据库、web服务器甚至操作系统的一部分封装到单个进程中(java)

当需要伸缩性是,就会碰到下面这个主要问题:几乎不可能把混为一体的进程所干的事情分解到许多不同的物理机器上,因此这类应用就必须要有极为强大的服务器来支撑。这些服务器,需要花费数万甚至数十万美金,从而使这类大规模Web网站远离了财政不宽裕的个人和小公司。

LAMP社区注意到,如果将Web栈分解为多个独立的组件,人们就能从容的从廉价的服务器开始自己的事业,而在发展壮大时只需要添加更多的廉价服务器。如果3000美元的数据库服务器不足以处理负载,只需要简单的购买第二个(或第三、第四个)直到足够。如果需要更多的存储空间,只需增加新的NFS服务器。

但是,为了使这个成为可能,Web应用必须不再假设由同一个服务器处理所有的请求,甚至处理单个请求的所有部分。在大规模LAMP(以及Django)的部署环境中,处理一个页面甚至会涉及到多达半打的服务器!这一条件会对各方面产生诸多影响,但可以归结到以下几点:

§ 不能在本地保存状态。也就是说,任何需要在多个请求间共享的数据都必须保存在某种形式的持久性存储(如数据库)或集中化缓存中。

§ 软件不能假设资源是本地的。例如,Web平台不能假设数据库运行于同一个服务器上;因此,它必须能够连接到远程数据库服务器。

§ Web栈中的每个部分都必须易于移动或复制。如果在部署时因为某种原因Apache不能工作,必须能够在最小的代价下切换到其它服务器。或者,在硬件层次,如果Web服务器崩溃,必须能够在最小的宕机时间内替换到另一台机器。请记住,整个哲学基于将应用部署在廉价的、商品化的硬件上。因此,本就应当预见到单个机器的失效。

就所期望的那样,Django或多或少透明的处理好了这件事情,没有一个部分违反这些原则。但是,了解架构设计背后的哲学,在我们处理伸缩性时将会有所裨益。

但这果真解决问题?

这一哲学可能在理论上(或者在屏幕上)看起来不错,但是否真的能解决问题?

好了,我们不直接回答这个问题,先请看一下将业务建立在上述架构上的公司的不完全列表。大家可能已经熟悉其中的一些名字:

§ Amazon

§ Blogger

§ Craigslist

§ Facebook

§ Google

§ LiveJournal

§ Slashdot

§ Wikipedia

§ Yahoo

§ YouTube

请允许我用当哈利遇见沙莉中的著名场景来比喻:他们有的我们也会有!

关于个人偏好的备注

在进入细节之前,短暂跑题一下。

开源运动因其所谓的宗教战争而闻名;许多墨水(以及墨盒、硒鼓等)被挥洒在各类争论上:文本编辑器(emacsvs.vi),操作系统(Linuxvs.Windowsvs. Mac OS),数据库引擎(MySQL vs. PostgreSQL),当然还包括编程语言。

我们希望远离这些战争。仅仅因为没有足够的时间。

但是,在部署Django确实有很多选择,而且我们也经常被问及个人偏好。表述这些会使社区处于引发上类战争的危险边缘,所以我们在一直以来非常克制。但是,出于完整性和全无保留的目的,我们将在这里表述自己的偏好。以下是我们的优先选择:

§ 操作系统: Linux(具体而言是Ubuntu)

§ Web服务器:Apachemod_python

§ 数据库服务器:PostgreSQL

当然,我们也看到,许多做了不同选择的Django用户同样也大获成功。

Apachemod_python来部署Django

目前,Apachemod_python是在生产服务器上部署Django的最健壮搭配。

mod_python (http://www.djangoproject.com/r/mod_python/)是一个在Apache中嵌入PythonApache插件,它在服务器启动时将Python代码加载到内存中。(译注:这里的内存是指虚拟内存)代码在Apache进程的整个生命周期中都驻留在内存中,与其它服务器的做法相比,这将带来重要的性能提升。

Django要求Apache2.xmod_python3.x,并且我们优先考虑Apacheprefork MPM模式,而不是worker MPM

备注

如何配置Apache超出了本书的范围,因此下面将只简单介绍必要的细节。幸运的是,如果需要进一步学习Apache的相关知识,可以找到相当多的绝佳资源。下面是我们所中意的部分资料:

§ 开源的Apache在线文档,位于http://www.djangoproject.com/r/apache/docs/

§ Pro Apache,第三版(Apress, 2004),作者Peter Wainwright,位于http://www.djangoproject.com/r/books/pro-apache/

§ Apache: The Definitive Guide,第三版(OReilly, 2002),作者Ben LauriePeter Laurie,位于http://www.djangoproject.com/r/books/apache-pra/

基本配置

为了配置基于 mod_python Django,首先要安装有可用的 mod_python模块的 Apache。这通常意味着应该有一个LoadModule指令在 Apache配置文件中。它看起来就像是这样:

LoadModule python_module /usr/lib/apache2/modules/mod_python.so

然后,编辑你的Apache的配置文件,添加如下内容:

<Location "/">

SetHandler python-program

PythonHandler django.core.handlers.modpython

SetEnv DJANGO_SETTINGS_MODULE mysite.settings

PythonDebug On

</Location>

要确保把DJANGO_SETTINGS_MODULE中的mysite.settings项目换成与你的站点相应的内容。

它告诉 Apache,任何在 /这个路径之后的 URL都使用 Django mod_python来处理。它DJANGO_SETTINGS_MODULE的值传递过去,使得 mod_python知道这时应该使用哪个配置。

注意这里使用<Location>指令而不是<Directory>。后者用于指向你的文件系统中的一个位置,然而<Location>指向一个 Web 站点的 URL 位置。

Apache 可能不但会运行在你正常登录的环境中,也会运行在其它不同的用户环境中;也可能会有不同的文件路径或 sys.path。你需要告诉 mod_python如何去寻找你的项目及 Django的位置。

PythonPath "['/path/to/project', '/path/to/django'] + sys.path"

你也可以加入一些其它指令,比如PythonAutoReload Off以提升性能。查看 mod_python文档获得详细的指令列表。

注意,你应该在成品服务器上设置PythonDebug Off。如果你使用PythonDebug On的话,在程序产生错误时,你的用户会看到难看的(并且是暴露的) Python 回溯信息。

重启 Apache之后所有对你的站点的请求(或者是当你用了<VirtualHost>指令后则是虚拟主机)都会由 Djanog来处理。

注意

如果你在一个比 /位置更深的子目录中部署 Django,它不会对你的 URL进行修整。所以如果你的 Apache配置是像这样的:

<Location "/mysite/">

SetHandler python-program

PythonHandler django.core.handlers.modpython

SetEnv DJANGO_SETTINGS_MODULE mysite.settings

PythonDebug On

</Location>

则你的所有URL都要以"/mysite/"起始。基于这种原因我们通常建议将 Django,部署在主机或虚拟主机的根目录。另外还有一个选择就是,你可以简单的将你的 URL配置转换成像下面这样:

urlpatterns = patterns('',

(r'^mysite/', include('normal.root.urls')),

)

在同一个 Apache的实例中运行多个 Django程序

在同一个 Apache实例中运行多个 Django程序是完全可能的。当你是一个独立的Web 开发人员并有多个不同的客户时,你可能会想这么做。

只要像下面这样使用VirtualHost你可以实现:

NameVirtualHost *

<VirtualHost *>

ServerName www.example.com

# ...

SetEnv DJANGO_SETTINGS_MODULE mysite.settings

</VirtualHost>

<VirtualHost *>

ServerName www2.example.com

# ...

SetEnv DJANGO_SETTINGS_MODULE mysite.other_settings

</VirtualHost>

如果你需要在同一个VirtualHost中运行两个 Django程序,你需要特别留意一下以确保 mod_python的代码缓存不被弄得乱七八糟。使用PythonInterpreter指令来将不同的<Location>指令分别解释:

<VirtualHost *>

ServerName www.example.com

# ...

<Location "/something">

SetEnv DJANGO_SETTINGS_MODULE mysite.settings

PythonInterpreter mysite

</Location>

<Location "/otherthing">

SetEnv DJANGO_SETTINGS_MODULE mysite.other_settings

PythonInterpreter mysite_other

</Location>

</VirtualHost>

这个PythonInterpreter中的值不重要,只要它们在两个Location块中不同。

mod_python运行一个开发服务器

因为 mod_python缓存预载入了 Python的代码,当在 mod_python上发布 Django 站点时,你每改动了一次代码都要需要重启 Apache一次。这还真是件麻烦事,所以这有个办法来避免它:只要加入MaxRequestsPerChild 1到配置文件中强制 Apache在每个请求时都重新载入所有的代码。但是不要在产品服务器上使用这个指令,这会撤销 Django的特权。

如果你是一个用分散的print语句(我们就是这样)来调试的程序员,注意这print句在 mod_python 中是无效的;它不会像你希望的那样产生一个 Apache日志。如果你需要在 mod_python中打印调试信息,可能需要用到Python 标准日志包(Pythons standard logging package)。更多的信息请参见http://docs.python.org/lib/module-logging.html。另一个选择是在模板页面中加入调试信息。

使用相同的Apache实例来服务DjangoMedia文件

Django本身不用来服务media文件;应该把这项工作留给你选择的网络服务器。我们推荐使用一个单独的网络服务器(即没有运行Django的一个)来服务media。想了解更多信息,看下面的章节。

不过,如果你没有其他选择,所以只能在同Django一样的ApacheVirtualHost上服务media文件,这里你可以针对这个站点的特定部分关闭mod_python

<Location "/media/">

SetHandler None

</Location>

Location改成你的media文件所处的根目录。

你也可以使用<LocationMatch>来匹配正则表达式。比如,下面的写法将Django定义到网站的根目录,并且显式地将media子目录以及任何以.jpg.gif或者.png结尾的URL屏蔽掉:

<Location "/">

SetHandler python-program

PythonHandler django.core.handlers.modpython

SetEnv DJANGO_SETTINGS_MODULE mysite.settings

</Location>

<Location "/media/">

SetHandler None

</Location>

<LocationMatch "\.(jpg|gif|png)$">

SetHandler None

</LocationMatch>

在所有这些例子中,你必须设置DocumentRoot,这样apache才能知道你存放静态文件的位置。

错误处理

当你使用 Apache/mod_python时,错误会被 Django捕捉,它们不会传播到 Apache那里,也不会出现在 Apache错误日志中。

有一个例外就是当确实你的 Django设置混乱了时。在这种情况下,你会在浏览器上看到一个内部服务器错误的页面,并在 Apache错误日志中看到 Python的完整回溯信息。错误日志的回溯信息有多行。当然,这些信息是难看且难以阅读的。

处理段错误

有时候,Apache会在你安装Django的时候发生段错误。这时,基本上总是有以下两个与Django本身无关的原因其中之一所造成:

§ 有可能是因为,你使用了pyexpat模块(进行XML解析)并且与Apache内置的版本相冲突。详情请见http://www.djangoproject.com/r/articles/expat-apache-crash/.

§ 也有可能是在同一个Apache进程中,同时使用了mod_python mod_php,而且都使用MySQL作为数据库后端。在有些情况下,这会造成PHPPythonMySQL模块的版本冲突。在mod_pythonFAQ中有更详细的解释。http://www.djangoproject.com/r/articles/php-modpython-faq/.

如果还有安装mod_python的问题,有一个好的建议,就是先只运行mod_python站点,而不使用Django框架。这是区分mod_python特定问题的好方法。下面的这篇文章给出了更详细的解释。http://www.djangoproject.com/r/articles/getting-modpython-working/.

下一个步骤应该是编辑一段测试代码,把你所有django相关代码import进去,你的views,models,URLconf,RSS配置,等等。把这些imports放进你的handler函数中,然后从浏览器进入你的URL。如果这些导致了crash,你就可以确定是importdjango代码引起了问题。逐个去掉这些imports,直到不再冲突,这样就能找到引起问题的那个模块。深入了解各模块,看看它们的imports。要想获得更多帮助,像linuxldconfigMac OSotoolwindowsListDLLsform sysInternals)都可以帮你识别共享依赖和可能的版本冲突。

使用FastCGI部署Django应用

尽管将使用Apachemod_python搭建Django环境是最具鲁棒性的,但在很多虚拟主机平台上,往往只能使用FastCGI

此外,在很多情况下,FastCGI能够提供比mod_python更为优越的安全性和效能。针对小型站点,相对于Apache来说FastCGI更为轻量级。

FastCGI 简介

如何能够由一个外部的应用程序很有效解释WEB服务器上的动态页面请求呢?答案就是使用FastCGI!它的工作步骤简单的描述起来是这样的: 1WEB服务器收到客户端的页面请求 2WEB服务器将这个页面请求委派给一个FastCGI外部进程(WEB服务器于FastCGI之间是通过socket来连接通讯的) 3FastCGI外部进程得到WEB服务器委派过来的页面请求信息后进行处理,并且将处理结果(动态页面内容)返回给WEB服务器 4Web服务器将FastCGI返回回来的结果再转送给客户端浏览器。

mod_python一样,FastCGI也是驻留在内存里为客户请求返回动态信息,而且也免掉了像传统的CGI一样启动进程时候的时间花销。但于mod_python不同之处是它并不是作为模块运行在web服务器同一进程内的,而是有自己的独立进程。

为什么要在一个独立的进程中运行代码?

在以传统的方式的几种以mod_*方式嵌入到Apache的脚本语言中(常见的例如:PHPPython/mod_pythonPerl/mod_perl),他们都是以apache扩展模块的方式将自身嵌入到Apache进程中的。尽管这种方式可以减低启动时候的时间花销(因为代码不用在每次收到访问请求的时候都读去硬盘数据),但这是以增大内存的开销来作为代价的。

每一个Apache进程都是一个Apache引擎的副本,它完全包括了所有Apache所具有的一切功能特性(哪怕是对Django毫无好处的东西也一并加载进来)。而FastCGI就不一样了,它仅仅把PythonDjango等必备的东东弄到内存中。

依据FastCGI自身的特点可以看到,FastCGI进程可以与Web服务器的进程分别运行在不同的用户权限下。对于一个多人共用的系统来说,这个特性对于安全性是非常有好处的,因为你可以安全的于别人分享和重用代码了。

如果你希望你的DjangoFastCGI的方式运行,那么你还必须安装flup这个Python库,这个库就是用于处理FastCGI的。很多用户都抱怨flup的发布版太久了,老是不更新。其实不是的,他们一直在努力的工作着,这是没有放出来而已。但你可以通过http://www.djangoproject.com/r/flup/获取他们的最新的SVN版本。

运行你的 FastCGI服务器

FastCGI是以客户机/服务器方式运行的,并且在很多情况下,你得自己去启动FastCGI的服务进程。 Web服务器(例如Apache,lighttpd等等)仅仅在有动态页面访问请求的时候才会去与你的Django-FastCGI进程交互。因为Fast-CGI已经一直驻留在内存里面了的,所以它响应起来也是很快的。

注意

在虚拟主机上使用的话,你可能会被强制的使用Web server-managed FastCGI进程。在这样的情况下,请参阅下面的Apache共享主机里运行Django”这一小节。

web服务器有两种方式于FastCGI进程交互:使用Unix domain socket(win32里面是命名管道)或者使用TCP socket.具体使用哪一个,那就根据你的偏好而定了,但是TCP socket弄不好的话往往会发生一些权限上的问题。

开始你的服务器项目,首先进入你的项目目录下(你的manage.py文件所在之处),然后使用manage.py runfcgi命令:

./manage.py runfcgi [options]

想了解如何使用runfcgi,输入manage.py runfcgi help命令。

你可以指定socket或者同时指定hostport。当你要创建Web服务器时,你只需要将服务器指向当你在启动FastCGI服务器时确定的socket或者host/port

范例:

TCP端口上运行一个线程服务器:

./manage.py runfcgi method=threaded host=127.0.0.1 port=3033

Unix socket上运行prefork服务器:

./manage.py runfcgi method=prefork socket=/home/user/mysite.sock pidfile=django.pid

启动,但不作为后台进程(在调试时比较方便):

./manage.py runfcgi daemonize=false socket=/tmp/mysite.sock

停止FastCGI的行程

如果你的FastCGI是在前台运行的,那么只需按Ctrl+C就可以很方便的停止这个进程了。但如果是在后台运行的话,你就要使用Unixkill命令来杀掉它。

如果你在manage.py runfcgi中指定了pidfile这个选项,那么你可以这样来杀死这个FastCGI后台进程:

kill `cat $PIDFILE`

$PIDFILE就是你在pidfile指定的那个。

你可以使用下面这个脚本方便地重启Unix里的FastCGI守护进程:

#!/bin/bash

# Replace these three settings.

PROJDIR="/home/user/myproject"

PIDFILE="$PROJDIR/mysite.pid"

SOCKET="$PROJDIR/mysite.sock"

cd $PROJDIR

if [ -f $PIDFILE ]; then

kill `cat -- $PIDFILE`

rm -f -- $PIDFILE

fi

exec /usr/bin/env - \

PYTHONPATH="../python:.." \

./manage.py runfcgi socket=$SOCKET pidfile=$PIDFILE

Apache中以FastCGI的方式使用Django

ApacheFastCGI上使用Django,你需要安装和配置Apache,并且安装mod_fastcgi。请参见Apachemod_fastcgi文档:http://www.djangoproject.com/r/mod_fastcgi/

当完成了安装,通过httpd.confApache的配置文件)来让ApacheDjango FastCGI互相通信。你需要做两件事:

§ 使用FastCGIExternalServer指明FastCGI的位置。

§ 使用mod_rewriteFastCGI指定合适的URL

指定 FastCGI Server的位置

FastCGIExternalServer告诉Apache如何找到FastCGI服务器。按照FastCGIExternalServer文档(http://www.djangoproject.com/r/mod_fastcgi/FastCGIExternalServer/),你可以指明socket或者host。以下是两个例子:

# Connect to FastCGI via a socket/named pipe:

FastCGIExternalServer /home/user/public_html/mysite.fcgi -socket /home/user/mysite.sock

# Connect to FastCGI via a TCP host/port:

FastCGIExternalServer /home/user/public_html/mysite.fcgi -host 127.0.0.1:3033

在这两个例子中, /home/user/public_html/目录必须存在,而/home/user/public_html/mysite.fcgi文件不一定存在。它仅仅是一个Web服务器内部使用的接口,这个URL决定了对于哪些URL的请求会被FastCGI处理(下一部分详细讨论)。

使用mod_rewriteFastCGI指定URL

第二步是告诉Apache为符合一定模式的URL使用FastCGI。为了实现这一点,请使用mod_rewrite模块,并将这些URL重定向到mysite.fcgi(或者正如在前文中描述的那样,使用任何在FastCGIExternalServer指定的内容)。

在这个例子里面,我们告诉Apache使用FastCGI来处理那些在文件系统上不提供文件(译者注:也就是指向虚拟文件)和没有从/media/开始的任何请求。如果你使用Djangoadmin站点,下面可能是一个最普通的例子:

<VirtualHost 12.34.56.78>

ServerName example.com

DocumentRoot /home/user/public_html

Alias /media /home/user/python/django/contrib/admin/media

RewriteEngine On

RewriteRule ^/(media.*)$ /$1 [QSA,L]

RewriteCond %{REQUEST_FILENAME} !-f

RewriteRule ^/(.*)$ /mysite.fcgi/$1 [QSA,L]

</VirtualHost>

FastCGI lighttpd

lighttpd (http://www.djangoproject.com/r/lighttpd/)是一个轻量级的Web服务器,通常被用来提供静态页面的访问。它天生支持FastCGI,因此除非你的站点需要一些Apache特有的特性,否则,lighttpd对于静态和动态页面来说都是理想的选择。

确保mod_fastcgi在模块列表中,它需要出现在mod_rewritemod_access,但是要在mod_accesslog之前。你也可能需要mod_alias模块,来提供管理所用的媒体资源。

将下面的内容添加到你的lighttpd的配置文件中:

server.document-root = "/home/user/public_html"

fastcgi.server = (

"/mysite.fcgi" => (

"main" => (

# Use host / port instead of socket for TCP fastcgi

# "host" => "127.0.0.1",

# "port" => 3033,

"socket" => "/home/user/mysite.sock",

"check-local" => "disable",

)

),

)

alias.url = (

"/media/" => "/home/user/django/contrib/admin/media/",

)

url.rewrite-once = (

"^(/media.*)$" => "$1",

"^/favicon\.ico$" => "/media/favicon.ico",

"^(/.*)$" => "/mysite.fcgi$1",

)

在一个lighttpd进程中运行多个Django站点

lighttpd允许你使用条件配置来为每个站点分别提供设置。为了支持FastCGI的多站点,只需要在FastCGI的配置文件中,为每个站点分别建立条件配置项:

# If the hostname is 'www.example1.com'...

$HTTP["host"] == "www.example1.com" {

server.document-root = "/foo/site1"

fastcgi.server = (

...

)

...

}

# If the hostname is 'www.example2.com'...

$HTTP["host"] == "www.example2.com" {

server.document-root = "/foo/site2"

fastcgi.server = (

...

)

...

}

你也可以通过fastcgi.server中指定多个入口,在同一个站点上实现多个Django安装。请为每一个安装指定一个FastCGI主机。

在使用Apache的共享主机服务商处运行Django

许多共享主机的服务提供商不允许运行你自己的服务进程,也不允许修改httpd.conf文件。尽管如此,仍然有可能通过Web服务器产生的子进程来运行Django

备注

如果你要使用服务器的子进程,你没有必要自己去启动FastCGI服务器。Apache会自动产生一些子进程,产生的数量按照需求和配置会有所不同。

在你的Web根目录下,将下面的内容增加到.htaccess文件中:

AddHandler fastcgi-script .fcgi

RewriteEngine On

RewriteCond %{REQUEST_FILENAME} !-f

RewriteRule ^(.*)$ mysite.fcgi/$1 [QSA,L]

接着,创建一个脚本,告知Apache如何运行你的FastCGI程序。创建一个mysite.fcgi文件,并把它放在你的Web目录中,打开可执行权限。

#!/usr/bin/python

import sys, os

# Add a custom Python path.

sys.path.insert(0, "/home/user/python")

# Switch to the directory of your project. (Optional.)

# os.chdir("/home/user/myproject")

# Set the DJANGO_SETTINGS_MODULE environment variable.

os.environ['DJANGO_SETTINGS_MODULE'] = "myproject.settings"

from django.core.servers.fastcgi import runfastcgi

runfastcgi(method="threaded", daemonize="false")

重启新产生的进程服务器

如果你改变了站点上任何的python代码,你需要告知FastCGI。但是,这不需要重启Apache,而只需要重新上传mysite.fcgi或者编辑改文件,使得修改时间发生了变化,它会自动帮你重启Django应用。

如果你拥有Unix系统命令行的可执行权限,只需要简单地使用touch命令:

touch mysite.fcgi

可扩展性

既然你已经知道如何在一台服务器上运行Django,让我们来研究一下,如何扩展我们的Django安装。这一部分我们将讨论,如何把一台服务器扩展为一个大规模的服务器集群,这样就能满足每小时上百万的点击率。

有一点很重要,每一个大型的站点大的形式和规模不同,因此可扩展性其实并不是一种千篇一律的行为。以下部分会涉及到一些通用的原则,并且会指出一些不同选择。

首先,我们来做一个大的假设,只集中地讨论在Apachemod_python下的可扩展性问题。尽管我们也知道一些成功的中型和大型的FastCGI策略,但是我们更加熟悉Apache

运行在一台单机服务器上

大多数的站点一开始都运行在单机服务器上,看起来像图20-1这样的构架。

20-1:一个单服务器的Django安装。

这对于小型和中型的站点来说还不错,并且也很便宜,一般来说,你可以在3000美元以下就搞定一切。

然而,当流量增加的时候,你会迅速陷入不同软件的资源争夺之中。数据库服务器和Web服务器都喜欢自己拥有整个服务器资源,因此当被安装在单机上时,它们总会争夺相同的资源(RAM, CPU),它们更愿意独享资源。

通过把数据库服务器搬移到第二台主机上,可以很容易地解决这个问题。这将在下一部分介绍。

分离出数据库服务器

对于Django来说,把数据库服务器分离开来很容易:只需要简单地修改DATABASE_HOST,设置为新的数据库服务器的IP地址或者DNS域名。设置为IP地址总是一个好主意,因为使用DNS域名,还要牵涉到DNS服务器的可靠性连接问题。

使用了一个独立的数据库服务器以后,我们的构架变成了图20-2

20-2:将数据库移到单独的服务器上。

这里,我们开始步入n-tier构架。不要被这个词所吓坏,它只是说明了Web栈的不同部分,被分离到了不同的物理机器上。

我们再来看,如果发现需要不止一台的数据库服务器,考虑使用连接池和数据库备份将是一个好主意。不幸的是,本书没有足够的时间来讨论这个问题,所以你参考数据库文档或者向社区求助。

运行一个独立的媒体服务器

使用单机服务器仍然留下了一个大问题:处理动态内容的媒体资源,也是在同一台机器上完成的。

这两个活动是在不同的条件下进行的,因此把它们强行凑和在同一台机器上,你不可能获得很好的性能。下一步,我们要把媒体资源(任何不是Django视图产生的东西)分离到别的服务器上(请看图20-3)。

20-3:分离出媒体服务器。

理想的情况是,这个媒体服务器是一个定制的Web服务器,为传送静态媒体资源做了优化。lighttpdtux (http://www.djangoproject.com/r/tux/)都是极佳的选择,当然瘦身的Apache服务器也可以工作的很好。

对于拥有大量静态内容(照片、视频等)的站点来说,将媒体服务器分离出去显然有着更加重要的意义,而且应该是扩大规模的时候所要采取的第一步措施

这一步需要一点点技巧,Djangoadmin管理接口需要能够获得足够的权限来处理上传的媒体(通过设置MEDIA_ROOT)。如果媒体资源在另外的一台服务器上,你需要获得通过网络写操作的权限。

最简单的方案是使用NFS(网络文件系统)把媒体服务器的目录挂载到Web服务器上来。只要MEDIA_ROOT设置正确,媒体的上传就可以正常工作。

实现负担均衡和数据冗余备份

现在,我们已经尽可能地进行了分解。这种三台服务器的构架可以承受很大的流量,比如每天1000万的点击率。如果还需要进一步地增加,你就需要开始增加冗余备份了。

这是个好主意。请看图 20-3,一旦三个服务器中的任何一个发生了故障,你就得关闭整个站点。因此在引入冗余备份的时候,你并不只是增加了容量,同时也增加了可靠性。

我们首先来考虑Web服务器的点击量。把同一个Django的站点复制多份,在多台机器上同时运行很容易,我们也只需要同时运行多台机器上的Apache服务器。

你还需要另一个软件来帮助你在多台服务器之间均衡网络流量:流量均衡器(load balancer。你可以购买昂贵的专有的硬件均衡器,当然也有一些高质量的开源的软件均衡器可供选择。

Apaches mod_proxy是一个可以考虑的选择,但另一个配置更棒的选择是:Perlbal (http://www.djangoproject.com/r/perlbal/)Perlbal是一个均衡器,同时也是一个反向代理,它的开发者和memcached的开发者是同一拨人(请见13章)。

备注

如果你使用FastCGI,你同样可以分离前台的web服务器,并在多台其他机器上运行FastCGI服务器来实现相同的负载均衡的功能。前台的服务器就相当于是一个均衡器,而后台的FastCGI服务进程代替了Apache/mod_python/Django服务器。

现在我们拥有了服务器集群,我们的构架慢慢演化,越来越复杂,如图20-4

20-4:负载均衡的服务器设置。

值得一提的是,在图中,Web服务器指的是一个集群,来表示许多数量的服务器。一旦你拥有了一个前台的均衡器,你就可以很方便地增加和删除后台的Web服务器,而且不会造成任何网站不可用的时间。

慢慢变大

下面的这些步骤都是上面最后一个的变体:

§ 当你需要更好的数据库性能,你可能需要增加数据库的冗余服务器。MySQL内置了备份功能;PostgreSQL应该看一下Slony (http://www.djangoproject.com/r/slony/) pgpool (http://www.djangoproject.com/r/pgpool/),这两个分别是数据库备份和连接池的工具。

§ 如果单个均衡器不能达到要求,你可以增加更多的均衡器,并且使用轮训(round-robinDNS来实现分布访问。

§ 如果单台媒体服务器不够用,你可以增加更多的媒体服务器,并通过集群来分布流量。

§ 如果你需要更多的高速缓存(cache),你可以增加cache服务器。

§ 在任何情况下,只要集群工作性能不好,你都可以往上增加服务器。

重复了几次以后,一个大规模的构架会像图20-5

20-5。大规模的Django安装。

尽管我们只是在每一层上展示了两到三台服务器,你可以在上面随意地增加更多。

当你到了这一个阶段,你有一些选择。附录A有一些开发者关于大型系统的信息。如果你想要构建一个高流量的Django站点,那值得一读。

性能优化

如果你有大笔大笔的钱,遇到扩展性问题时,你可以简单地投资硬件。对于剩下的人来说,性能优化就是必须要做的一件事。

备注

顺便提一句,谁要是有大笔大笔的钞票,请捐助一点Django项目。我们也接受未切割的钻石和金币。

不幸的是,性能优化比起科学来说更像是一种艺术,并且这比扩展性更难描述。如果你真想要构建一个大规模的Django应用,你需要花大量的时间和精力学习如何优化构架中的每一部分。

以下部分总结了多年以来的经验,是一些专属于Django的优化技巧。

RAM怎么也不嫌多

写这篇文章的时候,RAM的价格已经降到了每G大约200美元。购买尽可能多的RAM,再在别的上面投资一点点。

高速的处理器并不会大幅度地提高性能;大多数的Web服务器90%的时间都浪费在了硬盘IO上。当硬盘上的数据开始交换,性能就急剧下降。更快速的硬盘可以改善这个问题,但是比起RAM来说,那太贵了。

如果你拥有多台服务器,首要的是要在数据库服务器上增加内存。如果你能负担得起,把你整个数据库都放入到内存中。这不会很难。LJWorld.com等网站的数据库保存了大量从1989年起至今的报纸和文章,内存的消耗也不到2G

下一步,最大化Web服务器上的内存。最理想的情况是,没有一台服务器进行磁盘交换。如果你达到了这个水平,你就能应付大多数正常的流量。

禁用 Keep-Alive

Keep-AliveHTTP提供的功能之一,它的目的是允许多个HTTP请求复用一个TCP连接,也就是允许在同一个TCP连接上发起多个HTTP请求,这样有效的避免了每个HTTP请求都重新建立自己的TCP连接的开销。

这一眼看上去是好事,但它足以杀死Django站点的性能。如果你从单独的媒体服务器上向用户提供服务,每个光顾你站点的用户都大约10秒钟左右发出一次请求。这就使得HTTP服务器一直在等待下一次keep-alive的请求,空闲的HTTP服务器和工作时消耗一样多的内存。

使用 memcached

尽管Django支持多种不同的cache后台机制,没有一种的性能可以接近memcached。如果你有一个高流量的站点,不要犹豫,直接选择memcached

经常使用memcached

当然,选择了memcached而不去使用它,你不会从中获得任何性能上的提升。第13章将为你提供有用的信息:学习如何使用Djangocache框架,并且尽可能地使用它。大量的可抢占式的高速缓存通常是一个站点在大流量下正常工作的唯一瓶颈。

参加讨论

Django相关的每一个部分,从LinuxApachePostgreSQL或者MySQL背后,都有一个非常棒的社区支持。如果你真想从你的服务器上榨干最后1%的性能,加入开源社区寻求帮助。多数的自由软件社区成员都会很乐意地提供帮助。

别忘了Django社区。这本书谦逊的作者只是Django开发团队中的两位成员。我们的社区有大量的经验可以提供。


转载请注明文章出处:http://blog.csdn.net/wolaiye320/article/details/52800128

0 0
原创粉丝点击