非同期ツイストの同期コード、またはヘビとハリネズミを横断する方法についての物語

すべてが順調です


Twistedは、Pythonで記述された非同期(イベント指向)フレームワークです。 ネットワーク(だけでなく)サービスの迅速な開発のための強力なツール。 Reactorデザインパターンを使用して開発されています。 Twistedを使用して作成されたサービスは高速で信頼性が高く、このフレームワークにより、パスタコードを記述せずに、わかりにくいコールバックがいっぱいで、美しいヘルパー(遅延、トランスポート、プロトコルなど)があります。 言い換えれば、バックエンド開発者の生活は良くなっています。



しかし、問題があります


主な問題は、同期Pythonモジュール(ソケット、OS、SSL、時間、選択、スレッド、サブプロセス、システム、信号など)を使用する多数の信頼できるテスト済みの便利なライブラリが、基本的にメインプロセスを取得してブロックすることです、原子炉サイクルとトラブルが来るでしょう。 このようなライブラリは、たとえば、psycopg2、request、mysqlなどです。 特に、psycopg2はDjango ORMでデータベースバックエンドの1つとして使用されます。



どうする?


3つの方法があります。 洗練された、受け入れられ、良い。 複雑-Twistedでライブラリのアナログを実装します。 受け入れられるのは、deferToThreadを使用し、別のスレッドで同期コードを実行することです(Twistedで実装されたスレッドプールを使用)。 良い方法(私の意見では)はメモで議論されます。







グリーンストリームとイベントを使用してコンテキストを切り替えます!





これには何が必要ですか?








実際のプロジェクトでのテクノロジーの適用例




既成のソリューションを見つけてテストし、プロジェクトで実装したため、コードをグリーンレットに送信する機能を備えたリアクターの独自の実装を作成しませんでした。 Reactorコードはここから取得できます



アプリケーションの初期化時にgeventreactorを使用するには、インストールする必要があります。



from geventreactor import install install()
      
      







新しいメソッドが利用可能になりました:

 __all__ = ['deferToGreenletPool', 'deferToGreenlet', 'callMultipleInGreenlet', 'waitForGreenlet', 'waitForDeferred', 'blockingCallFromGreenlet', 'IReactorGreenlets', 'GeventResolver', 'GeventReactor', 'install']
      
      







reactor.deferToThread(f、* args、** kwargs)との類推により、factor.deferToGreenlet(f、* args、** kwargs)を呼び出すことができます。ここで、fは呼び出し可能なオブジェクトであり、* argsと** kwargsはその引数です。



動作させるには、名前空間のライブラリにもパッチを適用する必要があります。

 from gevent import monkey monkey.patch_all()
      
      







これらの操作の後、メインのPythonライブラリはGeventフレームワークによってパッチされます。 Geventのドキュメントを参照



これで、それらを直接インポートするすべてのライブラリまたはコードは、ロックメソッドまたはロック関数を呼び出すと、Geventシステムで対応するイベントをトリガーします。 これらのイベントでコールバックがハングアップし、グリーンレット間でコンテキストを切り替えることができます。



私のプロジェクトでは、Django ORMを使用してPostgreSQLのデータを操作します。 したがって、ORMメソッドがプロセスをブロックしないようにするには、データベース接続のプールを作成して接続を切り替えることができる特別なバックエンドを使用する必要があります。 バックエンドの1つはdjango-db-geventpoolです



django-db-geventpoolの使用は難しくありません。 ドキュメントに従ってください。



次は?




reactor.deferToGreenletメソッドはDeferredオブジェクトを返します。これは通常のDeferredとして使用できます。



たとえば、モデルがあります:



 class ExampleModel(models.Model): title = models.CharField(max_length=256)
      
      







すべてのモデルを取得し、それらをシステム内のプロセッサに渡す必要があります。 次のように書くことができます。

 d = reactor.deferToGreenlet(ExampleModel.objects.all)
      
      







そして、私たちのコードはメインプロセスをブロックしません。 実際、Django ORMがcursor.execute()を呼び出し、データベースドライバーからの応答を待機する瞬間に、geventreactorはコンテキストを別のDeferredに切り替えます。



結果は何ですか?




リアクターのイベントループをブロックすることなく、不要なスレッドやプロセスを作成することなく、Twisted内で同期コードを実行できます。 主なことは、非同期システムでの作業の基本原則に従うことです。コードの断片は長すぎて実行しないでください。geventを使用すると、コードの任意の場所からコンテキストの切り替えを強制できます。便利な場合は、gevent.sleep()を呼び出すだけです。



All Articles