2012-08-09 18 views
16

nginx + gunicorn + djangoの上に実装されたWebサービスで作業しています。クライアントはスマートフォンアプリケーションです。アプリケーションは、外部API(Facebook、Amazon S3 ...)を長時間呼び出す必要があります。そのため、サーバーは単にジョブサーバー(Celeryを使用してRedisを使用)にジョブをキューイングします。nginx/gunicorn/djangoウェブアーキテクチャで長時間実行されるHTTP接続の効率的な処理

サーバーがジョブをキューに入れたら、すぐに戻り、HTTP接続が閉じられます。これは正常に動作し、サーバーは非常に高い負荷を維持することができます。

client     server     job server 
    .      |      | 
    .      |      | 
    |------HTTP request----->|      | 
    |      |--------queue job------>| 
    |<--------close----------|      | 
    .      |      | 
    .      |      | 

しかし、場合によっては、クライアントはジョブが終了するとすぐに結果を取得する必要があります。残念ながら、HTTP接続が閉じられると、サーバーがクライアントに接続する方法はありません。 1つの解決策は、ジョブが完了するまで、数秒ごとにサーバーをポーリングするクライアント・アプリケーションに依存することです。可能であれば、このソリューションを避けたいのは、サービスの反応性を妨げ、また多くの不要なポーリング要求をサーバーにロードするためです。

要するに、私は何もしません(何もしないで、TCP接続を維持するために空白を毎回送信することを除いて、ちょうどlike Amazon S3 does)、ジョブが完了するまでサーバーは結果を返します。 、

client     server     job server 
    .      |      | 
    .      |      | 
    |------HTTP request----->|      | 
    |      |--------queue job------>| 
    |<------keep-alive-------|      | 
    |   [...]   |      | 
    |<------keep-alive-------|      | 
    |      |<--------result---------| 
    |<----result + close-----|      | 
    .      |      | 
    .      |      | 

は、どのように私はそれがまだそうではありません(サーバーが非常に高い負荷がかかっていると仮定すると、効率的な方法で実行時間の長いHTTP接続を実装することができますが、目標は、可能な限り最高の負荷を維持できるようにするには1秒あたり数百または数千の要求を伴う)?

実際のジョブを他のサーバーにオフロードすると、サーバーのCPU使用率を低く抑えることができますが、プロセスが重複してサーバーのすべてのRAMを使用したり、

おそらく、ほとんどの場合、nginxとgunicornを適切に設定する必要があります。私はちょっと読んだasync workers based on greenlets in gunicorn:ドキュメントは、非同期ワーカーが "アプリケーション(つまり、外部のWebサービス)、長い呼び出しをブロックすることによって使用されていると言う"、これは完璧な音。また、「」と表示されます。一般的に、アプリケーションは変更なしでこれらのワーカークラスを利用できるはずです "。これはすばらしいですね。これに関するフィードバック?

アドバイスいただきありがとうございます。

+0

あなたはdjangoのAJAX 'long-polling'ソリューションを調査しましたか?それは基本的に同じことのようです。 – dgel

+0

はい、そうです、おそらく同じことでしょう。 AJAXはクライアント側のjavascriptを意味し、Webブラウザに関するものです。私の場合、クライアントはソフトフォンアプリケーションです。しかし、サーバー側はおそらくほぼ同じ、優れたアイデアです。 – MiniQuark

答えて

28

私はanswering my own questionです。おそらく誰かがより良い解決策を持っています。

gunicorn's documentationを少し読んで、eventletgeventについてもう少し読んで、私はガンコンが私の質問に完全に答えると思います。 Gunicornには、労働者のプールを管理するマスタープロセスがあります。各ワーカーは、同期(シングルスレッド、一度に1つの要求を処理)または非同期(各ワーカーは実際にはほぼ同時に複数の要求を処理します)のいずれかになります。

同期ワーカーは非常に理解しやすく、デバッグするのが簡単です。ワーカーが失敗すると、1つの要求だけが失われます。しかし、長期間実行されている外部API呼び出しに苦労している場合、基本的にはスリープ状態です。したがって、負荷が高い場合、結果を待つ間にすべてのワーカーがスリープ状態になり、要求が破棄される可能性があります。

解決策は、デフォルトのワーカータイプを同期から非同期に変更することです(eventletまたはgeventを選択、here's a comparison)。現在、各作業者は複数の非常に軽量なgreen threadsを実行しています。 1つのスレッドがI/Oを待たなければならないときは、別のスレッドが実行を再開します。これはcooperative multitaskingと呼ばれます。非常に高速で、非常に軽量です(1人の作業者がI/Oを待っていると同時に何千もの同時要求を処理できます)。正確に私が必要なもの。

私は既存のコードをどのように変更すべきかと思っていましたが、明らかに標準のpythonモジュールは起動時に(実際にはイベントレットまたはgeventによって)monkey-patchedによってgunicornによって変更されています。

gunicornのworker_connectionsパラメータ、backlogパラメータを使用して、保留中の接続の最大数を使用して、たとえば、gunicornで同時クライアントの最大数を微調整可能なパラメータの束など

はこれがちょうどですがあります。素晴らしい、私はすぐにテストを開始します!

+0

テスト後のフィードバックや更新はありますか?あなたの足跡に従うことについて – dsldsl

+0

こんにちはdsldsl。私は今までのところ、ガンコーンとジーヴェントにとても満足しています。私はいくつかの簡単なテストを行いました。満足していましたが、gunicornは以下のように振る舞います。私は1人のワーカーを実行してみて、いくつかの接続を開いてみました。しかし、今まで私は実際のベンチマーキングを行う時間がなかった。楽しい! – MiniQuark