web-dev-qa-db-ja.com

Python、WSGI、マルチプロセッシング、共有データ

Mod_wsgiのマルチプロセッシング機能と、マルチプロセッシング機能を備えたWSGIサーバーで実行される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

マルチプロセッシング環境で予期しない動作をしますか?

ありがとうございました!

25
Zaur Nasibov

あなたの質問で考慮すべきいくつかの側面があります。

まず、ApacheMPMとmod_wsgiアプリケーション間の相互作用。 mod_wsgiアプリケーションを埋め込みモードで実行する場合(WSGIDaemonProcessは不要、WSGIProcessGroup %{GLOBAL})、ApacheMPMからマルチプロセッシング/マルチスレッドを継承します。これが最速のオプションであり、MPM構成に応じて、複数のプロセスとプロセスごとに複数のスレッドが存在することになります。逆に、mod_wsgiをデーモンモードでWSGIDaemonProcess <name> [options]WSGIProcessGroup <name>を使用して実行すると、小さな オーバーヘッド を犠牲にしてマルチプロセッシング/マルチスレッドを細かく制御できます。

単一のApache2サーバー内で、0、1つ、または複数の名前付きWSGIDaemonProcessesを定義でき、各アプリケーションはこれらのプロセスの1つ(WSGIProcessGroup <name>)で実行するか、WSGIProcessGroup %{GLOBAL}を使用して組み込みモードで実行できます。

wsgi.multithread変数とwsgi.multiprocess変数を調べることで、マルチプロセッシング/マルチスレッドを確認できます。

構成WSGIDaemonProcess example processes=5 threads=1には、5つの独立したプロセスがあり、それぞれが単一の実行スレッドを持っています。生成サブプロセスを制御できないため、グローバルデータも共有メモリもありませんが、mod_wsgiがそれを実行します。グローバル状態を共有するために、いくつかの可能なオプションをすでにリストしました:プロセスがインターフェースするDB、ある種のファイルシステムベースの永続性、デーモンプロセス(Apacheの外部で開始)およびソケットベースのIPC。

Roland Smithが指摘しているように、後者は高レベル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には有用なメソッドが登録されていないため、上記の例はダミーですが、 ここ (python docs)作成​​方法とproxyプロセス内のオブジェクト(例のcounterなど)。

processes=5 threads=1を使用した、例の最後のコメント。これは単なる例であることを理解していますが、実際のアプリケーションでは、パフォーマンスはprocesses=1 threads=5に関して同等になると思います。マルチプロセッシングでデータを共有する複雑さは、単一プロセスの多くのスレッドで期待されるパフォーマンスが向上する場合にのみ行う必要があります。 'モデルは重要です。

23
Stefano M

Wsgiのプロセスとスレッドに関するドキュメントから:

複数の子プロセスが存在するモードでApacheを実行すると、各子プロセスには各WSGIアプリケーションのサブインタープリターが含まれます。

つまり、構成では、それぞれ1つのスレッドを持つ5つのプロセスに、5つのインタープリターがあり、共有データはありません。カウンターオブジェクトは、各インタプリタに固有のものになります。セッションをカウントするためのカスタムソリューションを構築する必要があります(通信できる一般的なプロセスの1つ、永続性ベースのソリューションなど)。または、これは間違いなく私の推奨事項です。ビルド済みのソリューションを使用してください(GoogleAnalyticsとChartbeatは素晴らしいオプション)。

私は、グローバルを使用してデータを共有することを、グローバルな悪用の大きな形と考える傾向があります。これは、私が並列処理を行ったほとんどの環境でのバグであり、移植性の問題です。突然、アプリケーションが複数の仮想マシンで実行された場合はどうなりますか?これにより、スレッドとプロセスの共有モデルに関係なく、コードが破損します。

4
marr75

multiprocessingを使用している場合、プロセス間でデータを共有するための 複数の方法 があります。 および 配列 プロセスが親子関係を持っている場合にのみ機能します(プロセスは継承によって共有されます)。そうでない場合は、 Manager および Proxy オブジェクトを使用します。

2
Roland Smith