web-dev-qa-db-ja.com

イベント駆動型モデルとリアクターパターンの違いは何ですか?

ウィキペディアから Reactor Pattern の記事:

リアクター設計パターンは、1つ以上の入力によってサービスハンドラーに同時に配信されるサービス要求を処理するためのイベント処理パターンです。

いくつかの例を挙げました。 nodejstwistedeventmachine

しかし、私が理解しているのは、上記の人気のあるイベント駆動型フレームワークであるため、リアクターパターンフレームワークでもあるのでしょうか。

これら2つを区別する方法は?それとも同じですか?

73
Howard

リアクタパターンは、「イベント駆動型プログラミング」よりも具体的です。これは、イベント駆動型プログラミングを行うときに使用される特定の実装手法です。ただし、この用語は通常の会話ではあまり正確に使用されていないため、使用して視聴者に理解してもらうよう注意し、使用時に用語の解釈方法に注意する必要があります。

リアクタパターンを調べる1つの方法は、「ノンブロッキング」操作の概念に密接に関連するものを検討することです。特定の操作をブロックせずに完了すると、リアクターは通知を送信します。たとえば、select(2)を使用して、標準のBSDソケットAPI(recv(2)send(2)など)を使用してソケットの読み取りおよび書き込みを行うリアクタパターンを実装できます。 。 selectは、ソケットから即座にバイトを受信できるタイミングを通知します-たとえば、バイトはそのソケットのカーネルレシーババッファに存在するためです。

これらのアイデアを検討する際に検討したいもう1つのパターンは、proactorパターンです。リアクターパターンとは対照的に、プロクターパターンは、すぐに終了できるかどうかに関係なく、操作を開始し、非同期的に実行してから、完了に関する通知を配信します。

Windows I/O完了ポート(IOCP)APIは、proactorパターンを見ることができる1つの例です。 IOCPを使用してソケットで送信を実行すると、そのソケットのカーネル送信バッファーに空きがあるかどうかに関係なく、送信操作が開始されます。 WSASend呼び出しがすぐに完了する間、送信操作は(別のスレッド、おそらくカーネル内のスレッドで)続行されます。 sendactuallyが完了すると(送信されるバイトがそのソケットのカーネル送信バッファーにコピーされたことのみを意味する)、コールバック関数がWSASend呼び出しが呼び出されます(アプリケーションの新しいスレッドで)。

操作を開始し、完了時に通知されるこのアプローチは、asynchronous操作のアイデアの中心です。それをnon-blocking操作と比較して、操作を実行する直前に操作が完了するまで待機します。

どちらのアプローチも、イベント駆動型プログラミングに使用できます。リアクタパターンを使用して、プログラムは、(たとえば)ソケットのeventが読み取り可能になるのを待ってから読み取ります。 proactorパターンを使用して、プログラムは代わりに、ソケット読み取りのeventが完了するのを待ちます。

厳密に言えば、Twistedはreactorという用語を誤用しています。 select(2)twisted.internet.selectreactor)は、非ブロッキングI/Oを使用して実装されます。これは、非常にリアクターに似ています。ただし、アプリケーションコードに公開するインターフェイスはasynchronousであり、よりプロクターに似ています。 TwistedにはIOCPに基づくリアクターもあります。このリアクターは、同じ非同期のアプリケーション向けAPIを公開しますandは、プロクターのようなIOCP APIを使用します。プラットフォームごとに詳細が異なるこのハイブリッドアプローチでは、「リアクター」または「プロクター」という用語は特に正確ではありませんが、twisted.internet.reactorは基本的にノンブロッキングではなく完全に非同期です。proactorはおそらくより良い名前の選択でしょう。

125

「非同期」の主な意味は「非ブロッキング」であるため、この「非ブロッキング」と「非同期」の分離は間違っていると思います。 Reactorパターンは、非同期(つまり非ブロッキング)呼び出しに関するものですが、それらの呼び出しの同期(ブロッキング)処理です。 Proactorは、非同期(非ブロッキング)呼び出しと、それらの呼び出しの非同期(非ブロッキング)処理に関するものです。

6
emanuel

TCP接続を処理するために、2つの競合するWebアーキテクチャ、つまりスレッドベースのアーキテクチャとイベント駆動型アーキテクチャがあります。

スレッドベースのアーキテクチャ

マルチスレッドサーバーを実装する最も古い方法は、「接続ごとのスレッド」アプローチに従うことです。実行中のスレッドの数を制御および制限するために、単一のディスパッチャスレッドを、制限されたブロッキングキューおよびスレッドプールとともに使用できます。

ディスパッチャは、新しい接続用にTCPソケットでブロックし、制限付きブロッキングキューに提供します。TCPキューの境界を超える接続はドロップされ、望ましい予測可能なレイテンシで動作するために受け入れられた接続。

イベント駆動型アーキテクチャ

スレッドを接続から分離するイベント駆動型アーキテクチャでは、特定のハンドラーのイベントにのみスレッドを使用できます。

この創造的なコンセプトにより、Reactor Patternが棚から出て自慢できます。このアーキテクチャ上に構築されたシステムは、イベント作成者とイベントコンシューマで構成されます。

リアクターパターン

リアクタパターンは、TCP接続処理のためのイベント駆動型アーキテクチャの最も一般的な実装テクニックです。簡単に言えば、シングルスレッドイベントループを使用し、イベントをブロックし、それらのイベントを対応するハンドラー。

イベントのハンドラーがそれらを処理するために登録されている限り、他のスレッドがI/Oでブロックする必要はありません。 TCP接続を考慮すると、接続済み、入力準備完了、出力準備完了、タイムアウト、および切断済みのインスタンスをイベントに簡単に参照できます。

Reactorパターンは、モジュール化されたアプリケーションレベルのコードを再利用可能なReactor実装から分離します。それを実現するために、リアクタパターンのアーキテクチャは2つの重要な参加者で構成されています—リアクタとハンドラ。

原子炉

Reactorは個別のスレッドで実行され、適切な登録済みハンドラーに作業をディスパッチすることにより、接続済み、入力可能、出力可能、タイムアウト、切断などのI/Oイベントに反応します。

ハンドラー

ハンドラーは、I/Oイベントで実行する必要がある実際の作業または応答を実行します。 Reactorは、適切なハンドラーをディスパッチすることにより、I/Oイベントに応答します。

1995年に出版されたJim CoplienとDouglas C. Schmidtによる「Pattern Languages of Program Design」は、Reactor Patternを詳細に説明した本の1つです。

1