1. 程式人生 > >第十二章: 部署Django

第十二章: 部署Django

iter 細節 rep 控制 warning 創建 mes 性問題 pri

本章包含創建一個django程序最必不可少的步驟 在服務器上部署它

如果你一直跟著我們的例子做,你可能正在用runserver 但是runserver 要部署你的django程序,你需要掛接到工業用的服務器 如:Apache 在本章,我們將展示如何做,但是,在做之前我們要給你一個(要做的事的)清單.

準備你的代碼庫

很幸運,runserver 但是,在開始前,有一些**

關閉Debug模式.

我們在第2章,用命令 django-admin.py startproject創建了一個項目 , 其中創建的 settings.py 文件的 DEBUG 設置默認為 True

. django會根據這個設置來改變他們的行為, 如果 DEBUG 模式被開啟. 例如, 如果 DEBUG 被設置成 True , 那麽:

  • 所有的數據庫查詢將被保存在內存中, 以 django.db.connection.queries 的形式. 你可以想象,這個吃內存!

  • 任何404錯誤都將呈現django的特殊的404頁面(第3章有)而不是普通的404頁面。 這個頁面包含潛在的敏感信息,但是不會暴露在公共互聯網。

  • 你的應用中任何未捕獲的異常,從基本的python語法錯誤到數據庫錯誤以及模板語法錯誤都會返回漂亮的Django錯誤頁面。 這個頁面包含了比404錯誤頁面更多的敏感信息,所以這個頁面絕對不要公開暴露。

簡單的說,把`` DEBUG`` 設置成`` True`` 相當於告訴Django你的網站只會被可信任的開發人員使用。 Internet裏充滿了不可信賴的事物,當你準備部署你的應用時,首要的事情就是把`` DEBUG`` 設置為`` False`` 。

來關閉模板Debug模式。

類似地,你應該在生產環境中把TEMPLATE_DEBUGFalse 如果這個設為`` True`` ,為了在那個好看的錯誤頁面上顯示足夠的東西,Django的模版系統就會為每一個模版保存一些額外的信息。

實現一個404模板

如果`` DEBUG`` 設置為`` True`` ,Django會顯示那個自帶的404錯誤頁面。 但如果`` DEBUG`` 被設置成`` False`` ,那它的行為就不一樣了: 他會顯示一個在你的模版根目錄中名字叫`` 404.html`` 的模版 所以,當你準備部署你的應用時,你會需要創建這個模版並在裏面放一些有意義的“頁面未找到”的信息

這裏有一個`` 404.html``的示例,你可以從它開始。 假定你使用的模板繼承並定義一個 `` base.html``,該頁面由titlecontent兩塊組成。

{% extends "base.html" %}

{% block title %}Page not found{% endblock %}

{% block content %}
<h1>Page not found</h1>

<p>Sorry, but the requested page could not be found.</p>
{% endblock %}

要測試你的404.html頁面是否正常工作,僅僅需要將DEBUG 設置為`` False`` ,並且訪問一個並不存在的URL。 (它將在`` sunserver`` 上工作的和開發服務器上一樣好)

實現一個500模板

類似的,如果`` DEBUG`` 設置為`` False`` ,Djang不再會顯示它自帶的應對未處理的Python異常的錯誤反饋頁面。 作為代替,它會查找一個名為`` 500.html`` 的模板並且顯示它。 像`` 404.html`` 一樣,這個模板應該被放置在你的模板根目錄下。

這裏有一個關於500.html的比較棘手的問題。你永遠不能確定`` 為什麽``會顯示這個模板,所以它不應該做任何需要連接數據庫,或者依賴任何可能被破壞的基礎構件的事情。 (例如:它不應該使用自定義模板標簽。)如果它用到了模板繼承,那麽父模板也就不應該依賴可能被破壞的基礎構件。 因此,最好的方法就是避免模板繼承,並且用一些非常簡單的東西。 這是一個`` 500.html`` 的例子,可以把它作為一個起點:

<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN"
    "http://www.w3.org/TR/html4/strict.dtd">
<html lang="en">
<head>
    <title>Page unavailable</title>
</head>
<body>
    <h1>Page unavailable</h1>

    <p>Sorry, but the requested page is unavailable due to a
    server hiccup.</p>

    <p>Our engineers have been notified, so check back later.</p>
</body>
</html>

設置錯誤警告

當你使用Django制作的網站運行中出現了異常,你會希望去了解以便於修正它。 默認情況下,Django在你的代碼引發未處理的異常時,將會發送一封Email至開發者團隊。但你需要去做兩件事來設置這種行為。

首先,改變你的ADMINS設置用來引入你的E-mail地址,以及那些任何需要被註意的聯系人的E-mail地址。 這個設置采用了類似於(姓名, Email)元組,像這樣:

ADMINS = (
    (‘John Lennon‘, ‘[email protected]‘),
    (‘Paul McCartney‘, ‘[email protected]‘),
)

第二,確保你的服務器配置為發送電子郵件。 設置好postfix,sendmail或其他本書範圍之外但是與Django設置相關的郵件服務器,你需要將將 EMAIL_HOST設置為你的郵件服務器的正確的主機名. 默認模式下是設置為’localhost’, 這個設置對大多數的共享主機系統環境適用. 取決於你的安排的復雜性,你可能還需要設置 EMAIL_HOST_USER,EMAIL_HOST_PASSWORD,EMAIL_PORT或EMAIL_USE_TLS。

你還可以設置EMAIL_SUBJECT_PREFIX以控制Django使用的 error e-mail的前綴。 默認情況下它被設置為‘[Django]

設置連接中斷警報

如果你安裝有CommonMiddleware(比如,你的MIDDLEWARE_CLASSES設置包含了’django.middleware.common.CommonMiddleware’的情況下,默認就安裝了CommonMiddleware),你就具有了設置這個選項的能力:有人在訪問你的Django網站的一個非空的鏈接而導致一個404錯誤的發生和連接中斷的情況,你將收到一封郵件. 如果你想激活這個特性,設置SEND_BROKEN_LINK_EMAILS 為True(默認為False),並設置你的MANAGERS為某個人或某些人的郵件地址,這些郵件地址將會收到報告連接中斷錯誤的郵件. MANAGERS使用和ADMINS 同樣的語法.例如:

MANAGERS = (
    (‘George Harrison‘, ‘[email protected]‘),
    (‘Ringo Starr‘, ‘[email protected]‘),
)

請註意,錯誤的Email會令人感到反感,對於任何人來說都是這樣。

使用針對產品的不同的設置

在此書中,我們僅僅處理一個單一的設置文件 settings.py文件由django-admin.py startproject命令生成。但是當你準備要進行配置的時候,你將發現你需要多個配置文件以使你的開發環境和產品環境相獨立。 比如,你可能不想每次在本地機器上測試代碼改變的時候將DEBUG從False 改為True。Django通過使用多個配置文件而使得這種情況很容易得到避免。

如果你想把你的配置文件按照產品設置和開發設置組織起來,你可以通過下面三種方法的其中一種達到這個目的。

  • 設置成兩個全面的,彼此獨立的配置文件

  • 設置一個基本的配置文件(比如,為了開發)和第二個(為了產品)配置文件,第二個配置文件僅僅從基本的那個配置文件導入配置,並對需要定義的進行復寫.

  • 使用一個單獨的配置文件,此配置文件包含一個Python的邏輯判斷根據上下文環境改變設置。

我們將會在依次解釋這幾種方式

首先,最基本的方法是定義兩個單獨的配置文件。 如果你是跟隨之前的例子做下來的,那麽你已經有了一個settings.py了,現在你只需要將它復制一份並命名為settings_production.py(文件名可以按照你自己的喜好定義),在這個新文件中改變DEBUG等設置。

第二種方法比較類似,但是減少了許多冗余。 作為使用兩個內容大部分相同的配置文件的替代方式,你可以使用一個文件為基本文件,另外一個文件從基本文件中導入相關設定。 例如

# settings.py

DEBUG = True
TEMPLATE_DEBUG = DEBUG

DATABASE_ENGINE = ‘postgresql_psycopg2‘
DATABASE_NAME = ‘devdb‘
DATABASE_USER = ‘‘
DATABASE_PASSWORD = ‘‘
DATABASE_PORT = ‘‘

# ...

# settings_production.py

from settings import *

DEBUG = TEMPLATE_DEBUG = False
DATABASE_NAME = ‘production‘
DATABASE_USER = ‘app‘
DATABASE_PASSWORD = ‘letmein‘

此處,settings_production.py 從settings.py 導入所有的設定,僅僅只是重新定義了產品模式下需要特殊處理的設置。 在這個案例中,DEBUG 被設置為False,但是我們已經對產品模式設置了不同的數據庫訪問參數。 (後者將向你演示你可以重新定義 任何 設置,並不只是象 DEBUG 這樣的基本設置。)

最終,最精簡的達到兩個配置環境設定的方案是使用一個配置文件,在此配置文件中根據不同的環境進行設置。 一個達到這個目的的方法是檢查當前的主機名。 例如:

# settings.py

import socket

if socket.gethostname() == ‘my-laptop‘:
    DEBUG = TEMPLATE_DEBUG = True
else:
    DEBUG = TEMPLATE_DEBUG = False

# ...

在這裏,我們從python標準庫導入了socket 模塊,使用它來檢查當前系統的主機名。 我們可以通過檢查主機名來確認代碼是否運行在產品服務器上。

一個關鍵是配置文件僅僅是包含python代碼的文件。你可以從其他文件導入這些python代碼,可以通過這些代碼執行任意的邏輯判斷等操作。 如果你打算按照這種方案走下去,請確定這些配置文件中的代碼是足夠安全(防彈)的。 如果這個配置文件拋出任何的異常,Django都有可能會發生很嚴重的崩潰。

重命名settings.py

隨便將你的settings.py重命名為settings_dev.py或settings/dev.py或foobar.py,Django 並不在乎你的配置文件取什麽名字,只要你告訴它你使用的哪個配置文件就可以了。

但是如果你真的重命名了由django-admin.py startproject 命令創建的settings.py文件,你會發現manage.py會給出一個錯誤信息說找不到配置文件。 那是由於它嘗試從這個文件中導入一個叫做settings的模塊,你可以通過修改manage.py 文件,將 import settings 語句改為導入你自己的模塊,或者使用django-admin.py而不是使用manage.py,在後一種方式中你需要設置 DJANGO_SETTINGS_MODULE 環境變量為你的配置文件所在的python 路徑.(比如’mysite.settings’)。

DJANGO_SETTINGS_MODULE

通過這種方式的代碼改變後,本章的下一部分將集中在對具體環境(比如Apache)的發布所需要的指令上。 這些指令針對每一種環境都不同,但是有一件事情是相同的。 在每一種環境中,你都需要告訴Web服務器你的DJANGO_SETTINGS_MODULE是什麽,這是你的Django應用程序的進入點。 DJANGO_SETTINGS_MODULE指向你的配置文件,在你的配置文件中指向你的ROOT_URLCONF,在ROOT_URLCONF中指向了你的視圖以及其他的部分。

DJANGO_SETTINGS_MODULE是你的配置文件的python的路徑 比如,假設mysite是在你的Python路徑中,DJANGO_SETTINGS_MODULE對於我們正在進行的例子就是’mysite.settings’。

用Apache和mod_python來部署Django

目前,Apache和mod_python是在生產服務器上部署Django的最健壯搭配。

mod_python (http://www.djangoproject.com/r/mod_python/)是一個在Apache中嵌入Python的Apache插件,它在服務器啟動時將Python代碼加載到內存中。 (譯註:

Django 需要Apaceh 2.x 和mod_python 3.x支持。

備註

如何配置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 Laurie和Peter 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

Then, edit your Apache configuration file and add a <Location> directive that ties a specific URL path to a specific Django installation. 例如:

<Location "/">
    SetHandler python-program
    PythonHandler django.core.handlers.modpython
    SetEnv DJANGO_SETTINGS_MODULE mysite.settings
    PythonDebug Off
</Location>

要確保把 DJANGO_SETTINGS_MODULE 中的 mysite.settings 項目換成與你的站點相應的內容。

它告訴 Apache,任何在 / 這個路徑之後的 URL 都使用 Django 的 mod_python 來處理。 它 將 DJANGO_SETTINGS_MODULE 的值傳遞過去,使得 mod_python 知道這時應該使用哪個配置。

註意這裏使用 ```` 指令而不是 ```` 。 後者用於指向你的文件系統中的一個位置,然而 ````

System Message: WARNING/2 (<string>, line 403); backlink

Inline literal start-string without end-string.

System Message: WARNING/2 (<string>, line 403); backlink

Inline literal start-string without end-string.

System Message: WARNING/2 (<string>, line 403); backlink

Inline literal start-string without end-string.

System Message: WARNING/2 (<string>, line 403); backlink

Inline literal start-string without end-string.

System Message: ERROR/3 (<string>, line 405)

Unexpected indentation.

指向一個 Web 站點的 URL 位置。 ````

System Message: WARNING/2 (<string>, line 405); backlink

Inline literal start-string without end-string.

System Message: WARNING/2 (<string>, line 405); backlink

Inline literal start-string without end-string.

Apache 可能不但會運行在你正常登錄的環境中,也會運行在其它不同的用戶環境中;也可能會有不同的文件路徑或 sys.path。 你需要告訴 mod_python 如何去尋找你的項目及 Django 的位置。

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

你也可以加入一些其它指令,比如 PythonAutoReload Off 以提升性能。 查看 mod_python 文檔獲得詳細的指令列表。

註意,你應該在成品服務器上設置 PythonDebug Off 。如果你使用 PythonDebug On 的話,在程序產生錯誤時,你的用戶會看到難看的(並且是暴露的) Python 回溯信息。 如果你把 PythonDebug 置 On,當mod_python出現某些錯誤,你的用戶會看到醜陋的(也會暴露某些信息)Python的對錯誤的追蹤的信息。

重啟 Apache 之後所有對你的站點的請求(或者是當你用了 <VirtualHost> 指令後則是虛擬主機)都會由 Djanog 來處理。

在同一個 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實例來服務Django和Media文件

Django本身不用來服務media文件;應該把這項工作留給你選擇的網絡服務器。 我們推薦使用一個單獨的網絡服務器(即沒有運行Django的一個)來服務media。 想了解更多信息,看下面的章節。

不過,如果你沒有其他選擇,所以只能在同Django一樣的Apache VirtualHost 上服務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作為數據庫後端。 在有些情況下,這會造成PHP和Python的MySQL模塊的版本沖突。 在mod_python的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,你就可以確定是import的django代碼引起了問題。 逐個去掉這些imports,直到不再沖突,這樣就能找到引起問題的那個模塊。 深入了解各模塊,看看它們的imports。 要想獲得更多幫助,像linux的ldconfig,Mac OS的otool和windows的ListDLLs(form sysInternals)都可以幫你識別共享依賴和可能的版本沖突。

一種替代方案: mod_wsgi模塊

作為一個mod_python模塊的替代,你可以考慮使用mod_wsgi模塊(http://code.google.com/p/modwsgi/),此模塊開發的時間比mod_python的開發時間離現在更近一些,在Django社區已有一些使用。 一個完整的概述超出了本書的範圍,你可以從官方的Django文檔查看到更多的信息。

使用FastCGI部署Django應用

盡管將使用Apache和mod_python搭建Django環境是最具魯棒性的,但在很多虛擬主機平臺上,往往只能使用FastCGI

此外,在很多情況下,FastCGI能夠提供比mod_python更為優越的安全性和效能。 針對小型站點,相對於Apache來說FastCGI更為輕量級。

FastCGI 簡介

如何能夠由一個外部的應用程序很有效解釋WEB 服務器上的動態頁面請求呢? 答案就是使用FastCGI! 它的工作步驟簡單的描述起來是這樣的:

和mod_python一樣,FastCGI也是駐留在內存裏為客戶請求返回動態信息,而且也免掉了像傳統的CGI一樣啟動進程時候的時間花銷。 但於mod_python不同之處是它並不是作為模塊運行在web服務器同一進程內的,而是有自己的獨立進程。

為什麽要在一個獨立的進程中運行代碼?

在以傳統的方式的幾種以mod_*方式嵌入到Apache的腳本語言中(常見的例如: PHP,Python/mod_python和Perl/mod_perl),他們都是以apache擴展模塊的方式將自身嵌入到Apache進程中的。

每一個Apache進程都是一個Apache引擎的副本,它完全包括了所有Apache所具有的一切功能特性(哪怕是對Django毫無好處的東西也一並加載進來)。 而FastCGI就不一樣了,它僅僅把Python和Django等必備的東東弄到內存中。

依據FastCGI自身的特點可以看到,FastCGI進程可以與Web服務器的進程分別運行在不同的用戶權限下。 對於一個多人共用的系統來說,這個特性對於安全性是非常有好處的,因為你可以安全的於別人分享和重用代碼了。

如果你希望你的Django以FastCGI的方式運行,那麽你還必須安裝 flup 這個Python庫,這個庫就是用於處理FastCGI的。 很多用戶都抱怨 flup 的發布版太久了,老是不更新。 其實不是的,他們一直在努力的工作著,這是沒有放出來而已。

運行你的 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弄不好的話往往會發生一些權限上的問題。 What you choose is a manner of preference; a TCP socket is usually easier due to permissions issues.

開始你的服務器項目,首先進入你的項目目錄下(你的 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就可以很方便的停止這個進程了。 但如果是在後臺運行的話,你就要使用Unix的 kill 命令來殺掉它。 然而,當你正在處理後臺進程時,你會需要將其付諸於Unix kill的命令

如果你在 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

在Apache和FastCGI上使用Django,你需要安裝和配置Apache,並且安裝mod_fastcgi。 請參見Apache和mod_fastcgi文檔: http://www.djangoproject.com/r/mod_fastcgi/ 。

當完成了安裝,通過 httpd.conf (Apache的配置文件)來讓Apache和Django FastCGI互相通信。 你需要做兩件事:

  • 使用 FastCGIExternalServer 指明FastCGI的位置。

  • 使用 mod_rewrite 為FastCGI指定合適的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_rewrite為FastCGI指定URL

第二步是告訴Apache為符合一定模式的URL使用FastCGI。 為了實現這一點,請使用mod_rewrite 模塊,並將這些URL重定向到 mysite.fcgi (或者正如在前文中描述的那樣,使用任何在 FastCGIExternalServer 指定的內容)。

在這個例子裏面,我們告訴Apache使用FastCGI來處理那些在文件系統上不提供文件(譯者註:

<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 之前。

將下面的內容添加到你的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應用。 你可以重新上傳mysite.fcgi或者編輯這個文件以改變該文件的時間戳。 當阿帕奇服務器發現文檔被更新了,它將會為你重啟你的Django應用。

如果你擁有Unix系統命令行的可執行權限,只需要簡單地使用 touch 命令:

touch mysite.fcgi

可擴展性

既然你已經知道如何在一臺服務器上運行Django,讓我們來研究一下,如何擴展我們的Django安裝。 這一部分我們將討論,如何把一臺服務器擴展為一個大規模的服務器集群,這樣就能滿足每小時上百萬的點擊率。

有一點很重要,每一個大型的站點大的形式和規模不同,因此可擴展性其實並不是一種千篇一律的行為。 以下部分會涉及到一些通用的原則,並且會指出一些不同選擇。

首先,我們來做一個大的假設,只集中地討論在Apache和mod_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服務器,為傳送靜態媒體資源做了優化。 lighttpd和tux (http://www.djangoproject.com/r/tux/) 都是極佳的選擇,當然瘦身的Apache服務器也可以工作的很好。

對於擁有大量靜態內容(照片、視頻等)的站點來說,將媒體服務器分離出去顯然有著更加重要的意義,而且應該是擴大規模的時候所要采取的 第一步措施

這一步需要一點點技巧,Django的admin管理接口需要能夠獲得足夠的權限來處理上傳的媒體(通過設置 MEDIA_ROOT )。如果媒體資源在另外的一臺服務器上,你需要獲得通過網絡寫操作的權限。 如果你的應用牽涉到文件上載,Django需要能夠面向媒體服務器撰寫上載媒體 如果媒體是在另外一臺服務器上的,你需要部署一種方法使得Django可以通過網絡去寫這些媒體。

實現負擔均衡和數據冗余備份

現在,我們已經盡可能地進行了分解。 這種三臺服務器的構架可以承受很大的流量,比如每天1000萬的點擊率。

這是個好主意。 請看圖 20-3,一旦三個服務器中的任何一個發生了故障,你就得關閉整個站點。 因此在引入冗余備份的時候,你並不只是增加了容量,同時也增加了可靠性。

我們首先來考慮Web服務器的點擊量。 把同一個Django的站點復制多份,在多臺機器上同時運行很容易,我們也只需要同時運行多臺機器上的Apache服務器。

你還需要另一個軟件來幫助你在多臺服務器之間均衡網絡流量: 流量均衡器(load balancer) 。你可以購買昂貴的專有的硬件均衡器,當然也有一些高質量的開源的軟件均衡器可供選擇。

Apaches 的 mod_proxy 是一個可以考慮的選擇,但另一個配置更棒的選擇是: memcached是同一個團隊的人寫的一個負載均衡和反向代理的程序.(見第15章)

記錄

如果你使用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-robin)DNS來實現分布訪問。

  • 如果單臺媒體服務器不夠用,你可以增加更多的媒體服務器,並通過集群來分布流量。

  • 如果你需要更多的高速緩存(cache),你可以增加cache服務器。

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

重復了幾次以後,一個大規模的構架會像圖20-5。

技術分享圖片

圖 20-5。 大規模的Django安裝。

盡管我們只是在每一層上展示了兩到三臺服務器,你可以在上面隨意地增加更多。

性能優化

如果你有大筆大筆的錢,遇到擴展性問題時,你可以簡單地投資硬件。 對於剩下的人來說,性能優化就是必須要做的一件事。

註意

順便提一句,誰要是有大筆大筆的鈔票,請捐助一點Django項目。 我們也接受未切割的鉆石和金幣。

不幸的是,性能優化比起科學來說更像是一種藝術,並且這比擴展性更難描述。 如果你真想要構建一個大規模的Django應用,你需要花大量的時間和精力學習如何優化構架中的每一部分。

以下部分總結了多年以來的經驗,是一些專屬於Django的優化技巧。

RAM怎麽也不嫌多

最近即使那些昂貴的RAM也相對來說可以負擔的起了。 購買盡可能多的RAM,再在別的上面投資一點點。

高速的處理器並不會大幅度地提高性能;大多數的Web服務器90%的時間都浪費在了硬盤IO上。 當硬盤上的數據開始交換,性能就急劇下降。 更快速的硬盤可以改善這個問題,但是比起RAM來說,那太貴了。

如果你擁有多臺服務器,首要的是要在數據庫服務器上增加內存。 如果你能負擔得起,把你整個數據庫都放入到內存中。 這應該不是很困難,我們已經開發過一個站點上面有多於一百萬條報刊文章,這個站點使用了不到2GB的空間。

下一步,最大化Web服務器上的內存。 最理想的情況是,沒有一臺服務器進行磁盤交換。 如果你達到了這個水平,你就能應付大多數正常的流量。

禁用 Keep-Alive

Keep-Alive 是HTTP提供的功能之一,它的目的是允許多個HTTP請求復用一個TCP連接,也就是允許在同一個TCP連接上發起多個HTTP請求,這樣有效的避免了每個HTTP請求都重新建立自己的TCP連接的開銷。

這一眼看上去是好事,但它足以殺死Django站點的性能。 如果你從單獨的媒體服務器上向用戶提供服務,每個光顧你站點的用戶都大約10秒鐘左右發出一次請求。 這就使得HTTP服務器一直在等待下一次keep-alive 的請求,空閑的HTTP服務器和工作時消耗一樣多的內存。

使用 memcached

盡管Django支持多種不同的cache後臺機制,沒有一種的性能可以 接近 memcached。 如果你有一個高流量的站點,不要猶豫,直接選擇memcached。

經常使用memcached

當然,選擇了memcached而不去使用它,你不會從中獲得任何性能上的提升。 Chapter 15 is your best friend here: 學習如何使用Django的cache框架,並且盡可能地使用它。 大量的可搶占式的高速緩存通常是一個站點在大流量下正常工作的唯一瓶頸。

參加討論

Django相關的每一個部分,從Linux到Apache到PostgreSQL或者MySQL背後,都有一個非常棒的社區支持。 如果你真想從你的服務器上榨幹最後1%的性能,加入開源社區尋求幫助。 多數的自由軟件社區成員都會很樂意地提供幫助。

別忘了Django社區。 這本書謙遜的作者只是Django開發團隊中的兩位成員。 我們的社區有大量的經驗可以提供。

下一章

下面的章節集中在其他的一些Django特性上,你是否需要它們取決於你的應用項目。 可以自由選擇閱讀。

第十二章: 部署Django