翻译:merlin
How to use Django with FastCGI
尽管目前推荐的运行Django的方法是使用Apache加上mod_python,但是很多人使用了shared hosting,如此只能用FastCGI。某些情形下,FastCGI拥有更好的安全性——并且,可能比mod_python具有更好的性能。
本质上,FastCGI是一种让web服务器之外的独立程序提供页面的方法。Web服务器把受到的web请求通过socket委托给FastCGI,后者执行代码并返回结果给web服务器,然后服务器将结果返回给用户的浏览器。
如同mod_python一样,FastCGI也是把代码load到内存中,这样在对请求进行响应时将没有启动延时。但是和mod_python(或者mod_perl)不一样的是,FastCGI进程不是运行在web服务器之内,而是独立的进程。
为何它要自己一个人玩?
Apache经典的mod_* 参数,用来内嵌各种各样的脚本支持(比如PHP,Python和Perl),但是这些都是在web服务器的进程空间之内的。尽管这样节省了启动时间——代 码处理无须I/O操作,但是消耗了内存。以mod_python为例,每个apache进程都有自己的python解释器,这会占用大量内存。
根据FastCGI的天性,甚至允许运行非web服务器帐号的用户进程。这带来了额外的安全好处,因为你可以将你的代码与其他用户隔离开来。
前提条件:flup
在玩转Django搭配FastCGI之前,你必须安装flup,这是处理FastCGI的一个python库。请确保使用最新的SVN版本:-)
启动你的FastCGI服务器
FastCGI 以client-server的模式进行工作,大部分情况下,你自行启动FastCGI进程。你的web服务器(可以是apache,lighttpd, 或者别的什么)仅仅在需要载入动态页面的时候才去联系你的Django-FastCGI进程。因为它已经在内存中待命,所以处理响应的速度是非常快的。
注意
如果你玩的是共享主机系统,你可能被强制以web服务器管理的方式使用FastCGI进程。往下看,如何处理这种情形。
Web服务器有两种和你的FastCGI服务器发生联系的方法:要么通过Unix域套接字(windows上则是命名管道),要么直接通过TCP套接字。哪一种更好无所谓,取决于个人口味;只不过出于权限考虑,TCP套接字方法更为简单一些。
启动你的服务器之前,先到你的project目录下(也就是manage.py文件所在的那个目录啦),运行:
./manage.py runfcgi [options]
当然你可以用help选项来看看帮助信息。 当然,你需要指定一个socket或者主机加端口,这才能开搞。然后设置web服务器以完成启动FastCGI服务器。举例如下:
在指定的TCP端口上运行一个threaded服务器:
./manage.py runfcgi method=threaded host=127.0.0.1 port=3033
在指定的Unix域套接字上运行preforked服务器:
./manage.py runfcgi method=prefork socket=/home/user/mysite.sock pidfile=django.pid
以非daemon方式运行一个进程(便于调试):
./manage.py runfcgi daemonize=false socket=/tmp/mysite.sock
终止FastCGI daemon:
如果是放在前台运行,显然很简单:直接Ctrl-C之;如果是后台,也很显然:kill之。
如果启动的时候指定了pidfile选项,则可以这样kill之:
kill `cat $PIDFILE`
下面的shell脚本可以很方便地重起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的配置
当然,我们肯定需要配置了mod_fastcgi的apache。在此不再赘述,参见apache的有关文档。
通过编辑httpd.conf文件来让apache指向Django的FastCGI实例,你需要做如下两件事情:
- 使用FastCGIExternalServer directive来指明FastCGI server的位置
- 使用mod_rewrite来适当指明FastCGI上的URLs
关于指明FastCGI server的位置:
FastCGIExternalServer directive告诉 Apache 如何去找到你的FastCGI server。这里 FastCGIExternalServer docs 解释了,你可以选择指定一个套接字或者主机。这里是一些例子:
# 通过socket / named pipe来连接Connect to FastCGI
FastCGIExternalServer /home/user/public_html/mysite.fcgi -socket /home/user/mysite.sock
# 通过TCP host/port来连接FastCGI
FastCGIExternalServer /home/user/public_html/mysite.fcgi -host 127.0.0.1:3033
无论哪种情况,/home/user/public_html/mysite.fcgi 这个文件无须存在。 这只不过是web服务器内部使用的一个URL而已—— 用来指明FastCGI必须处理哪一个URL的一个钩子(下面细谈)。
用 mod_rewrite 指明FastCGI上的URLs
第二步是让apache知道对于特定的模式匹配的URLs该交给FastCGI。为此,使用 mod_rewrite模块并且重写 URLs 到 mysite.fcgi (或者任何你在FastCGIExternalServer directive中指明的东西,正如上面一节解释的那样)。
下面的例子中,我们告诉apache,对于所有非文件存取并且不以/media/打头的请求,就让FastCGI去处理。如果我们在Django中使用admin这个feature的话,这可能是最普通的情况:
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]
lighttpd 配置
lighttpd是一个轻量级的web服务器,通常用来存取静态文件。它天生支持FastCGI,因此,如果你对apache没有特别要求的话,这是个很好的选择,不管是静态页面还是动态页面。
确保 mod_fastcgi 已经安装,在 mod_rewrite和 mod_access之后的某个地方,但是不要在 mod_accesslog之后。你可能还需要 mod_alias,以便支持 admin media。
在你的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用 "conditional configuration" 来支持主机级的定制配置。为了配置多个FastCGI站点,只需在FastCGI配置文件中为每个站点加上下面的conditional block :
# 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 directive中指定多个entry来支持同一个站点上的多个Django 安装,还有为每个Django加上一个FAstCGI主机。
在Apache的共享主机中运行Django :
一般情况下,共享主机业主不允许你运行自己的服务器进程,也不允许你编辑httpd.conf配置文件。这时,对于Web server-spawned processes仍然可以运行Django。
注意事项
如果你遇到的是 Web server-spawned processes的情况,并不需要自己去启动FastCGI服务器。Apache 会根据情况衍生出一堆进程以满足需要。
在你的Web根目录下,加一个文件: .htaccess
AddHandler fastcgi-script .fcgi
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ mysite.fcgi/$1 [QSA,L]
然后,创建一个小脚本告诉Apache如何spawn你的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")
重起spawned server
如果你改变了任何python代码,你就需要告诉FastCGI已经动了代码。但是没有必要重起apache。相反,你只需上传 mysite.fcgi文件,或者编辑它一下,让这个文件的时间戳变一下。这样当apache看到这个文件更新过了,他会主动去重起你的Django程 序。
如果你有权限在Unix系统上玩到shell,用下面这条简单的touch命令就可以做到:
touch mysite.fcgi