並行性に対処するためにPythonに固執するか、放棄する必要がありますか?


31

私はDjangoで書かれた10K LOCプロジェクトを持っています。非同期性と必要なバックグラウンドジョブのためにかなりのセロリRabbitMQ)があり、並行性を高めるためにシステムの一部がDjango以外で書き直されることで恩恵を受けるという結論に達しました。理由は次のとおりです。

  • シグナル処理および可変オブジェクト。特に、あるシグナルが別のシグナルをトリガーする場合、ORMを使用してDjangoでそれらを処理すると、インスタンスが変更または消滅したときに驚く可能性があります。渡されたデータがハンドラー内で変化しないメッセージングアプローチを使用したいと思います(Clojureのコピーオンライトアプローチは、それが正しければ素晴らしいと思われます)。
  • システムの一部はWebベースではないため、タスクを同時に実行するためのより良いサポートが必要です。たとえば、システムはNFCタグを読み取り、読み取りが行われるとLEDが数秒間点灯し(Celeryタスク)、サウンドが再生され(他のCeleryタスク)、データベースが照会されます(他のタスク)。これはDjango管理コマンドとして実装されますが、DjangoとそのORMは本質的に同期しており、メモリを共有するのは制限されています(NFCリーダーを追加することを考えています。より優れたメッセージパッシング機能を確認したい)。

ErlangClojureなどの言語を使用する場合と比較して、TwistedTornadoなどを使用する場合の長所と短所は何ですか?私は実用的な利益と不利益に興味があります。

システムの一部が別の言語でより良くなるという結論にどのように到達しましたか?パフォーマンスに問題がありますか?これらの問題はどの程度深刻ですか?より高速にできる場合、高速であることが重要ですか?

例1: HTTPリクエストの外部で動作するDjango:

  1. NFCタグが読み取られます。
  2. データベース(および場合によってはLDAP)がクエリされ、データが利用可能になったときに何かを実行したい(赤または緑のライト、サウンドを再生する)。これはDjango ORMの使用をブロックしますが、利用可能なCeleryワーカーが存在する限り問題ではありません。より多くのステーションで問題になる場合があります。

例2: Djangoシグナルを使用した「メッセージの受け渡し」:

  1. post_deleteイベントが処理され、他の目的は、このために変更または削除することができます。
  2. 最後に、通知をユーザーに送信する必要があります。ここで、通知ハンドラーに渡される引数が削除済みまたは削除予定のオブジェクトのコピーであり、ハンドラーで変更されないことが保証されていると便利です。(もちろん、ORMが管理するオブジェクトをハンドラーに渡さないことで、手動で行うことができます。)

結論に至った理由をもっと説明すると、より良い答えが得られると思います
ウィンストン・エワート

5
言語選択の質問はトピック外であると誰かが言う前に、私はこれは特定の要件を伴う実際的な問題であるため、この質問は問題ないと思います。詳細な比較が行われることを願っています。
アダムリア

ツイストは concurrent の反対です!これは、イベント駆動型のシングルスレッドサーバーです。真の同時実行性が必要な場合、どこにもアクセスできません。

回答:


35

オープニング思考

システムの一部が別の言語でより良くなるという結論にどのように到達しましたか?パフォーマンスに問題がありますか?これらの問題はどの程度深刻ですか?より高速にできる場合、高速であることが重要ですか?

シングルスレッド非同期

シングルスレッドの非同期性とマルチスレッドの同時性の違い、長所、短所をすでに扱っているいくつかの質問と他のウェブリソースがあります。I / Oが主要なボトルネックであり、多数のリクエストが同時に処理される場合のNode.jsのシングルスレッド非同期モデルの動作について読むのは興味深いです。

Twisted、Tornado、およびその他の非同期モデルは、シングルスレッドをうまく利用しています。多くのWebプログラミングには大量のI / O(ネットワーク、データベースなど)があるため、リモートコールの待機に費やされる時間が大幅に増えます。それは、他のデータベース呼び出しの開始、ページのレンダリング、データの生成など、他のことを行うために費やすことができる時間です。そのシングルスレッドの使用率は非常に高くなっています。

シングルスレッド非同期の最大の利点の1つは、使用するメモリがはるかに少ないことです。マルチスレッド実行では、各スレッドに一定量の予約メモリが必要です。スレッドの数が増えると、スレッドが存在するために必要なメモリ量も増えます。メモリは有限であるため、一度に作成できるスレッドの数には限界があります。


Webサーバーの場合、各リクエストに独自のスレッドが与えられているふりをします。各スレッドに1MBのメモリが必要であり、Webサーバーには2GBのRAMがあるとします。このWebサーバーは、これ以上処理するのに十分なメモリがなくなる前の任意の時点で(およそ)2000件の要求を処理できます。

負荷がこれよりも大幅に高い場合、リクエストは非常に長い時間がかかる(古いリクエストが完了するのを待つ場合) 。


マルチスレッド同時実行

マルチスレッドの同時実行性は、代わりに複数のタスクを同時に実行することに依存しています。つまり、データベース呼び出しが戻るのを待ってスレッドがブロックされている場合、他の要求を同時に処理できます。スレッドの使用率は低くなりますが、実行するスレッドの数ははるかに多くなります。

また、マルチスレッドコードを推論するのははるかに困難です。ロック、同期、およびその他の楽しい同時実行性の問題があります。シングルスレッドの非同期は同じ問題の影響を受けません。

マルチスレッドコードは、CPUを集中的使用する場合のパフォーマンスがはるかに高いただし、するタスクでは、。通常はブロックするネットワークコールなど、スレッドが「譲る」機会がない場合、シングルスレッドモデルは同時実行性をまったく持ちません。

両方が共存する可能性があります

もちろん、2つの間に重複があります。それらは相互に排他的ではありません。たとえば、マルチスレッドコードは、各スレッドをより有効に活用するために、ノンブロッキング方式で記述できます。


ボトムライン

考慮すべき他の多くの問題がありますが、私はこのような2つについて考えるのが好きです:

  • プログラムがI / Oバウンドの場合である場合、シングルスレッド非同期はおそらく非常にうまく機能するでしょう。
  • プログラムがCPUバウンドの場合、おそらくマルチスレッドシステムが最適です。

特定のケースでは、完了している非同期作業の種類と、それらのタスクが発生する頻度を判断する必要があります。

  • リクエストごとに発生しますか?その場合、リクエストの数が増えるにつれて、メモリが問題になる可能性があります。
  • これらのタスクは注文されていますか?その場合、複数のスレッドを使用する場合は同期を検討する必要があります。
  • これらのタスクはCPUを集中的に使用しますか?もしそうなら、シングルスレッドは負荷に追いつくことができますか?

簡単な答えはありません。ユースケースとは何かを検討し、それに応じて設計する必要があります。非同期シングルスレッドモデルの方が良い場合があります。それ以外の場合、多数のスレッドを使用して大規模な並列処理を実現する必要があります。

その他の考慮事項

選択した同時実行モデルだけでなく、考慮する必要がある他の問題もあります。ErlangまたはClojureを知っていますか?これらの言語のいずれかで安全なマルチスレッドコードを記述して、アプリケーションのパフォーマンスを向上させることができると思いますか?これらの言語の1つに慣れるのに長い時間がかかりますか?また、学習した言語は将来あなたに利益をもたらすでしょうか?

これら2つのシステム間の通信に関連する問題はどうですか?2つの別々のシステムを並行して維持するのは非常に複雑ですか?ErlangシステムはどのようにしてDjangoからタスクを受け取りますか?Erlangはこれらの結果をDjangoにどのように伝えますか?パフォーマンスは、複雑さを増すだけの価値があるほど問題になりますか?


最終的な考え

私はいつもDjangoが十分に速いことを発見しており、非常にトラフィックの多いサイトで使用されています。同時要求数と応答時間を増やすために、いくつかのパフォーマンス最適化を行うことができます。確かに、私はこれまでCeleryで何もしていませんので、通常のパフォーマンスの最適化では、おそらくこれらの非同期タスクで発生する可能性のある問題は解決しません。

もちろん、問題に対してより多くのハードウェアを投入するという提案が常にあります。新しいサーバーをプロビジョニングするコストは、まったく新しいサブシステムの開発および保守コストよりも安いですか?

私はこの時点であまりにも多くの質問をしましたが、それは私の意図でした。答えは、分析とさらなる詳細なしでは簡単ではありません。しかし、問題を分析できるようになると、尋ねるべき質問を知ることになります。

私の直感では、別の言語で書き直す必要はないという。複雑さとコストはおそらく多すぎるでしょう。


編集

フォローアップへの対応

あなたのフォローアップは、いくつかの非常に興味深いユースケースを提示します。


1. HTTPリクエストの外部で動作するDjango

最初の例では、NFCタグを読み取り、データベースにクエリを実行しました。データベースまたはLDAPサーバーへのクエリはネットワークI / O(および潜在的にデータベースのパフォーマンス)によって制限されるため、この部分を別の言語で書くことはそれほど便利だとは思いません。一方、各管理コマンドは独自のプロセスとして実行されるため、同時要求の数はサーバー自体によってバインドされます。すでに実行中のプロセスにメッセージを送信していないため、セットアップとティアダウン時間がパフォーマンスに影響します。ただし、それぞれが独立したプロセスになるため、複数の要求を同時に送信できます。

この場合、調査できる2つの方法があります。

  1. 接続プーリングを使用して、データベースが複数のクエリを一度に処理できることを確認してください。(たとえば、Oracleでは、それに応じてDjangoを構成する必要があります'OPTIONS': {'threaded':True}。)データベースレベルまたはDjangoレベルで、独自のデータベースに合わせて調整できる同様の構成オプションがある場合があります。データベースクエリを作成する言語に関係なく、このデータが返されるのを待ってからLEDを点灯させる必要があります。ただし、クエリコードのパフォーマンスは違いもたらす可能性があり、Django ORMは非常に高速ではありません(ただし、通常は十分高速です)。
  2. セットアップ/分解時間を最小限にします。プロセスを常に実行し、それにメッセージを送信します。(私が間違っている場合は修正してください。しかし、これはあなたの元の質問が実際に焦点を当てているものです。)管理コマンドを頻繁に使用するという考えは好きではありません。NFCリーダーからのメッセージをメッセージキューにプッシュし、Celeryが読み取ってDjangoに転送する小さなコードを継続的に実行することは可能ですか?小さなプログラムのセットアップと分解は、たとえそれがPythonで書かれていても(Djangoではありません!)、Djangoプログラム(そのすべてのサブシステムを含む)を開始および停止するよりも優れているはずです。

DjangoにどのWebサーバーを使用しているかわかりません。mod_wsgifor Apacheでは、リクエストを処理するプロセスおよびプロセス内のスレッドの数を構成できます。Webサーバーの関連する構成を調整して、サービス可能な要求の数を最適化してください。


2. Djangoシグナルによる「メッセージの受け渡し」

2番目のユースケースも非常に興味深いものです。その答えがあるかどうかはわかりません。モデルインスタンスを削除していて、後で操作したい場合は、それらJSON.dumpsをシリアル化してから逆シリアル化することができJSON.loadsます。関連フィールドはデータベースから遅延ロードされ、そのリンクはもはや存在しないため、後でオブジェクトグラフを完全に再作成(関連モデルのクエリ)することは不可能です。

もう1つのオプションは、何らかの方法でオブジェクトを削除対象としてマークし、要求/応答サイクルの最後(すべての信号が処理された後)にのみ削除することです。に依存するのではなく、これを実装するにはカスタム信号が必要になる場合がありpost_deleteます。


1
多くのFUDと、ロックやその他のErlangの問題ではないことについての疑問、リストされている従来の共有状態の問題は、状態を共有しないように特別に設計された言語とランタイムの考慮事項ではありません。Erlangは非常に小さなRAMで何万もの個別のプロセスを処理できます。メモリの負荷も問題ではありません。

@Jarrod、私は個人的にErlangを知らないので、その点に関してあなたの言うことを受け入れます。そうでなければ、私が言及した他のほぼすべてが関連しています。コスト、複雑さ、および現在のツールが適切に利用されているかどうか。
ジョシュスミートン


これは私が本当に読むのが大好きな一種の壮大な答えです^^。+1、良い仕事です!
ローランブルゴー=ロイ

また、DJangoテンプレートがある場合は、アーランでErlydtl
Zachary K

8

主要な米国のISP向けに、非常に洗練された高度にスケーラブルな開発を行いました。Twistedサーバーを使用していくつかの深刻なトランザクション番号を実行しましたが、CPU /バインドされたものでPython / Twistedを拡張するのは複雑な悪夢でした。I / Oバウンドは問題ではありませんが、CPUバウンドは不可能です。システムを迅速にまとめることはできましたが、数百万人の同時ユーザーに拡張することは、CPUに縛られている場合、設定と複雑さの悪夢でした。

それについてのブログ記事、Python / Twisted VS Erlang / OTPを書きました。

TLDR; アーランが勝った。


4

Twistedの実際的な問題(私は大好きで、約5年間使用しています):

  1. ドキュメンテーションは、望まれる何かを残し、モデルはとにかく学ぶのが非常に複雑です。他のPythonプログラマーがTwistedコードを操作するのは難しいと思います。
  2. 優れたブロッキングAPIが不足しているため、ファイルI / Oとデータベースアクセスをブロックすることになりました。これは本当にパフォーマンスを損なう可能性があります。
  3. Twistedを使用する巨大なコミュニティや健全なコミュニティは存在しないようです。たとえば、Node.jsは、特にWebバックエンドプログラミング向けに、より積極的な開発を行っています。
  4. まだPythonであり、少なくともCPythonは最速のものではありません。

Node.jsとCoffeeScriptを使用して少し作業を行いましたが、同時実行パフォーマンスが懸念される場合は、飛躍する価値があるかもしれません。

Djangoの複数のインスタンスを実行して、インスタンス間でクライアントを分散するための何らかの調整を検討しましたか?


1
一般的な葉のPythonドキュメント望まれることを何か:/(それはだと言っていないという悪いが、言語の人気があること、それはかなり良くするために1が期待します)。
ルーク

3
Pythonのドキュメント、特にDjangoのドキュメントは、あらゆる言語に最適なドキュメントであると思います。しかし、多くのサードパーティのライブラリは、望まれるものを残しています。
ジョシュスミートン

1

別の言語への切り替えを検討する前に、次のことをお勧めします。

  1. LTTngを使用して、ページフォールト、コンテキストスイッチ、システムコール待機などのシステムイベントを記録します。
  2. Cライブラリを使用するのに時間がかかりすぎる場所はすべて変換しselect、そこにあるI / Oに適した任意のデザインパターン(マルチスレッド、シグナルイベントベース、コールバック非同期、または従来のUnix )を使用します。

アプリケーションのパフォーマンスが優先されると、Pythonでスレッドを使用しません。上記のオプションを使用すると、ソフトウェアの再利用、Djangoとの接続、パフォーマンス、開発の容易さなどの多くの問題を解決できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.