2012-10-03 83 views
18

我對mod_wsgi的多重處理特性和WSGI應用程序的一般設計有點困惑,它們將在具有多處理能力的WSGI服務器上執行。Python,WSGI,多處理和共享數據

考慮以下指令:

WSGIDaemonProcess example processes=5 threads=1 

如果正確地明白的mod_wsgi將產生5的Python(例如CPython的)進程和任何這些方法中的可以接收來自用戶的請求。

文檔說:

如果共享數據必須是可見的所有應用程序實例,不管他們執行其子進程,並 所做的更改一個應用程序的數據立即可用另一個, 包括在另一個子進程中執行的任何內容,必須使用外部數據(如數據庫或共享內存)。普通Python模塊中的全局變量 不能用於此目的。

但是在這種情況下,當需要確定應用程序在任何WSGI條件(包括多處理程序條件)下運行時,它會變得非常沉重。

例如,一個包含當前連接用戶數量的簡單變量 - 它應該是過程安全的,從memcached讀取/寫入,還是寫入DB,或者(如果這種不符合標準的庫機制可用)共享內存?

,並且將代碼像

counter = 0 

@app.route('/login') 
def login(): 
    ... 
    counter += 1 
    ... 

@app.route('/logout') 
def logout(): 
    ... 
    counter -= 1 
    ... 

@app.route('/show_users_count') 
def show_users_count(): 
    return counter 

行爲無法預測在多處理環境?

謝謝!

+5

引用:「一個包含當前連接用戶數量的簡單變量」。這是HTTP,沒有「連接」用戶的概念,所以這樣的計數不能「簡單」。 (例如,用戶可以通過忘記您提供給他們的任何令牌來註銷 - 例如,通過清除他們的瀏覽器cookie)。 –

+1

這意味着應用程序視爲「連接」的用戶,例如通過上一個HTTP會話時間戳+10分鐘。 –

+0

+1對安德魯的評論,但雖然我同意會話計數的固有困難,但我認爲這與良好的網頁設計有關,而不是特定的多處理/共享數據問題。另一個問題是,沒有代碼來確保計數器正在以有序的方式被讀取,更新和寫回(據我所知,+ = 1不是Python中的原子操作...)。需要某種鎖定。 – marr75

回答

18

在你的問題中有幾個方面需要考慮。

首先,apache MPM和mod_wsgi應用程序之間的交互。如果以嵌入模式運行mod_wsgi應用程序(不需要WSGIDaemonProcessWSGIProcessGroup %{GLOBAL}),您將繼承apache MPM的多處理/多線程。這應該是最快的選擇,並且每個進程最終擁有多個進程和多個線程,具體取決於您的MPM配置。相反,如果在守護進程模式下運行mod_wsgi,並且使用WSGIDaemonProcess <name> [options]WSGIProcessGroup <name>,則可以以小的overhead爲代價對多處理/多線程進行良好控制。

在單一的apache2服務器用戶還可以定義零個,一個或多個命名WSGIDaemonProcess ES,和每個應用程序可以在這些過程中的一個(WSGIProcessGroup <name>)來運行,或以嵌入模式與WSGIProcessGroup %{GLOBAL}運行。

您可以通過檢查wsgi.multithread和變量來檢查多處理/多線程。

您的配置WSGIDaemonProcess example processes=5 threads=1你有5個獨立的過程,每執行一個線程:沒有全球性的數據,沒有共享內存,因爲你是不是在產卵子流程的控制,但mod_wsgi的是做給你。爲了共享一個全局狀態,你已經列出了一些可能的選項:你的進程接口的數據庫,某種基於文件系統的持久性,守護進程(在apache之外啓動)以及基於套接字的IPC。

正如羅蘭·斯密所指出的,後者可以使用高層次的API由multiprocessing.managers實現:外Apache下,可以創建和啓動BaseManager服務器進程

m = multiprocessing.managers.BaseManager(address=('', 12345), authkey='secret') 
m.get_server().serve_forever() 

和你內心的應用,您connect

m = multiprocessing.managers.BaseManager(address=('', 12345), authkey='secret') 
m.connect() 

上面的例子是假的,因爲m沒有有用的方法註冊,但here(python文檔),你會發現如何創建和代理您的進程中有一個對象(如您示例中的counter)。

對你的例子的最後評論,processes=5 threads=1。我知道這僅僅是一個例子,但是在真實世界的應用程序中,我懷疑性能可以與processes=1 threads=5相媲美:只有當預期性能提升超過單進程許多線程時,您才應該進入多處理中共享數據的複雜性'模式是重要的。

+0

謝謝Stefano! –

+0

不客氣!我編輯了我的答案,以明確討論適用於由mod_wsgi運行的應用程序,而不是整個mod_wsgi,正如我的措辭可能提示的那樣。 –

3

從上處理文檔和螺紋,用於WSGI:

當Apache以模式下運行,從而有多個的子進程,每個子進程將包含每個WSGI應用子翻譯。

這意味着在您的配置中,每個進程有5個線程,將會有5個解釋器並且沒有共享數據。您的反制對象對每個解釋器都是唯一的。您需要構建一些自定義解決方案來統計會話(一個可以與之通信的常見流程,某種基於持久性的解決方案等),或者,這絕對是我的建議,使用預先構建的解決方案(Google Analytics和Chartbeat是夢幻般的選擇)。

我傾向於認爲使用全局變量來共享數據是一種大規模的全球性濫用形式。在我已經完成了並行處理的大多數環境中,這是一個bug和可移植性問題。如果突然你的應用程序要在多個虛擬機上運行,​​該怎麼辦?無論線程和進程的共享模式如何,這都會破壞您的代碼。

+0

這是通過chartbeat api查看您的網站流量的頁面(如果您需要以編程方式使用此數據):http://chartbeat.com/docs/api/explore/#endpoint=live/summary/v3/ – marr75

+0

謝謝您的回答,marr! –

1

如果您使用的是multiprocessing,則有multiple ways在進程之間共享數據。 ValuesArrays僅當進程具有父/子關係(它們通過繼承共享)纔有效。如果不是這種情況,請使用ManagerProxy對象。