3

クライアント(Ember.js)がWebSocket(私はFlask-SocketIOを使用しています)経由でサーバーと通信するPython Webアプリケーションを持っています。 (tesseract使用)PythonとEventletで複数のコアを使用する

  • は、クライアントから
  • OCRの着信画像(graphicsmagickを使用して)いくつかの画像変換を行う: アパートのWebSocketサーバからバックエンドは言及される価値がある2つのことを行います

    クライアントがイメージを送信すると、エンティティがデータベースに作成され、IDがイメージ変換キューに格納されます。作業者はそれをつかんで画像変換を行う。その後、作業者はそれをOCRキューに入れ、そこでOCRキュー・ワーカーによって処理されます。

    これまでのところとても良いです。 WSリクエストは別々のスレッドで同期して処理されます(Flask-SocketIOではEventletが使用されます)、重い計算アクションは非同期で発生します(別々のスレッドでも同様です)。

    問題点:アプリケーション全体がRaspberry Pi 3で実行されています。私が4つのコアを使用していない場合は、1つのARMv8コアが1.2GHzでクロックされているだけです。これはOCRの力はほとんどありません。だから私はPythonで複数のコアを使う方法を見つけることにしました。私はGILの問題について読んだが、multiprocessingについてはThe multiprocessing package offers both local and remote concurrency, effectively side-stepping the Global Interpreter Lock by using subprocesses instead of threads.と出ていた。まさに私が望んでいたもの。だから私は即座に

    from multiprocessing import Process 
    process = Process(target=heavy_computational_worker_thread) 
    process.start() 
    

    私は

    import multiprocessing 
    queue = multiprocessing.Queue() 
    

    from queue import Queue 
    queue = multiprocessing.Queue() 
    

    を変更しなければならなかったようにも複数のコアで処理されるために必要なキューによって

    from threading import Thread 
    thread = Thread(target=heavy_computational_worker_thread) 
    thread.start() 
    

    を置き換えます

    も同様である。問題:キューとスレッドライブラリは、Eventletによってmonkey patchedです。私が猿のパッチを当てたバージョンのThread and Queueの使用をやめ、代わりにmultiprocsssingのものを使用すると、キューにアクセスするときにEventletブロックによって開始されたリクエストスレッドが永遠にブロックされます。

    は今、私の質問:

    は、私は、このアプリケーションが別々のコア上のOCRと画像変換を行うことができますどのような方法がありますか?

    可能であれば、WebSocketとEventletを使いたいと思います。私が持っている利点は、プロセス間の唯一の通信インターフェイスがキューになることです。

    私がすでに持っていたアイデア: - Pythonのキュー実装ではなく、I/Oを使用しています。たとえば、異なるサブプロセスがアクセスする専用のRedis - さらにステップを進める:すべてのキューワーカーを個別のPythonプロセスとして開始する(例:python3 wsserver | python3 ocrqueue | python3 imgconvqueue)。次に、キューとデータベースのアクセスが非ブロックであることを自分自身で確認しなければならないでしょう。

    最高のことは、単一プロセスを維持してマルチプロセッシングで動作させることです。

+0

私はEvenletライブラリを知らないので、私の答えは当てはまりません。 「メイン」プログラムでマルチスレッドを使用する。そのメインアプリケーションの各「プロセス」内で、サブプロセスを呼び出すために「サブプロセス」を使用します(これは奇妙に思えますが、PythonプログラムはPythonプログラムを呼び出します)。これらのサブプロセスでのみ、 "Eventlet"ライブラリ(またはプロセスセーフでないライブラリ)を使用してください。メインプログラムでは使用しないでください。 「キュー」を使用することはできませんが、ファイルを介してデータを渡すことができます(例:プログラムAはイメージファイルを書き込み、終了し、プログラムBは開始し、このファイルを読み取ります)。 –

答えて

3

Eventletは、マルチプロセッシングパッケージで、現在は互換性がありません事前にありがとうございました。この作品には未解決の問題があります:https://github.com/eventlet/eventlet/issues/210

あなたのケースでうまくいくと思う選択肢は、Celeryを使用してキューを管理することです。 Celeryは、メイン・プロセスが提供するタスクをメッセージ・キュー(RabbitMQとRedisの両方がサポートされています)を介して待機するワーカー・プロセスのプールを開始します。

セレントワーカーは、イベントレットを使用する必要はありません。メインサーバーだけが行います。これにより、イベントレットの制限なしに何か必要なことを行うことができます。

このアプローチを検討することに興味があれば、それを使用する完全な例があります:https://github.com/miguelgrinberg/flack

+0

ありがとう、これは魅力のように動作します。私が今直面している唯一の問題は、もちろん、複数のプロセス間で共有することができないグローバル変数にsocketioインスタンスがあることです。しかし、これは、それを共有するために専用ストレージを使用するか、またはタスク関数に変数を与えることによって解決できなければなりません。 – Schnodderbalken

+0

私の答えで参照したプロジェクトを参照してください。ワーカープロセスから放出する必要がある場合は、各プロセスでsocketioインスタンスを作成します。違いは、Celeryワーカーのsocketioインスタンスには、関連付けられているFlaskアプリケーションインスタンスがないため、それらはemitのみであることです。プロセス間で何も共有する必要はありません。すべての通信はメッセージキューを介して行われます。 – Miguel

+0

https://github.com/miguelgrinberg/Flask-SocketIO/blob/master/docs/index.rstに記載されている手順に従ってください - 問題を解決するパラメータとしてmessage_queue = 'redis://'を使用してコンストラクタを使用して:)このようにして、私はまだsocketioのインスタンスを1つしか持たないので、ワーカープロセスから出すことができます。 – Schnodderbalken

関連する問題