Orleans でストリームを使用する理由
ストリーム処理システムを構築できるテクノロジは既に幅広く存在します。 たとえば、ストリーム データを永続的に格納するシステム (Event Hubs、Kafka など) や、ストリーム データに対するコンピューティング操作を表現するシステム (Azure Stream Analytics、Apache Storm、Apache Spark Streaming など) があります。 これらは、効率的なデータ ストリーム処理パイプラインを構築できる優れたシステムです。
既存システムの限界
しかしながら、これらのシステムは、ストリーム データに対するきめ細かい自由形式の計算には適していません。 前述のストリーミング コンピューティング システムでは、すべてのストリーム項目に同じ方法で適用される操作の統合データフロー グラフを指定できます。 これは、データが統一されていて、このデータに対して同じ一連の変換、フィルター処理、または集計操作を表現したい場合には強力なモデルです。 しかし、他にも、異なるデータ項目に対して基本的に異なる操作を表現する必要があるユース ケースがあります。 また、この処理の一部として、任意の REST API を呼び出すなど、外部呼び出しが必要になる場合もあります。 統合データフロー ストリーム処理エンジンは、これらのシナリオをサポートしていないか、限定的かつ制約された方法でサポートしているか、それらのサポートが役に立ちません。 これは、本質的に、大量の類似項目向けに最適化されており、通常は表現力や処理の点で制限されるためです。 Orleans ストリームは、そうした他のシナリオを対象としています。
目的
Orleans ユーザーが、グレイン メソッドの呼び出しで一連の項目を返すようにサポートしてほしいと要望したのがすべての始まりでした。 ご想像のとおり、それは氷山の一角にすぎませんでした。 それ以上のものを必要としていたのです。
Orleans ストリームの一般的なシナリオは、ユーザーごとにストリームを用意して、個々のユーザーのコンテキスト内でユーザーごとに異なる処理を実行する場合です。 ユーザーは何百万人もいるかもしれませんが、その中には、天気に関心があり、特定の場所の気象アラートを購読できるユーザーもいれば、スポーツ イベントに関心があるユーザーや、特定のフライトの状態を追跡しているユーザーもいます。 これらのイベントを処理するには異なるロジックが必要ですが、ストリーム処理の独立したインスタンスを 2 つ実行したくはありません。 一部のユーザーは、ある特定の株式にのみ、そして特定の外部条件が適用される場合にのみ関心があり、条件が必ずしもストリーム データに含まれるとは限りません (したがって、処理の一部として実行時に動的にチェックする必要があります)。
ユーザーの関心はひっきりなしに変わるので、特定のイベント ストリームへのサブスクリプションは動的に増減し、ストリーミング トポロジは動的かつ迅速に変化します。 その上、ユーザーごとの処理ロジックも、ユーザーの状態や外部イベントに基づいて動的に進化および変化します。 外部イベントによって、特定のユーザーに対する処理ロジックが変更される場合があります。 たとえば、ゲームの不正行為検出システムでは、不正行為の新たな方法が検出されると、この新しい違反を検出するために、処理ロジックを新しいルールで更新する必要があります。 これはもちろん、進行中の処理パイプラインを中断することなく行われる必要があります。 一括データフロー ストリーム処理エンジンは、そのようなシナリオをサポートするようには構築されていません。
こういうシステムは、単一のノードではなく、複数のネットワークに接続されたマシンで実行する必要があることは言うまでもないでしょう。 そのため、処理ロジックをサーバーのクラスター全体にスケーラブルかつ弾力的な方法で分散する必要があります。
新しい要件
ストリーム処理システムが上記のシナリオを対象にできるようにする 4 つの基本要件を特定しました。
- 柔軟なストリーム処理ロジック
- 高度に動的なトポロジのサポート
- きめ細かいストリームの細分性
- Distribution
柔軟なストリーム処理ロジック
ストリーム処理ロジックを表現するさまざまな方法をサポートするシステムが必要です。 前述の既存システムでは、開発者は、通常は関数型プログラミング スタイルに従って、宣言型のデータフロー計算グラフを記述する必要があります。 この場合、処理ロジックの表現力と柔軟性が制限されます。 Orleans ストリームは、処理ロジックの表現方法には構いません。 これはデータフローとして (たとえば、.NET の Reactive Extensions (Rx) を使用)、関数型プログラムとして、宣言型クエリとして、または一般的な命令型ロジックで表現できます。 ロジックはステートフルでもステートレスでもよく、副作用がある場合とない場合があり、外部アクションをトリガーできます。 開発者がすべてを決定できます。
動的トポロジのサポート
動的に進化するトポロジを可能にするシステムが必要です。 前述の既存システムは、通常、デプロイ時に固定され、実行時に進化できない静的トポロジのみに制限されています。 次のデータフロー式の例では、変更が必要になるまでは、すべてがすばらしく、シンプルです。
Stream.GroupBy(x=> x.key).Extract(x=>x.field).Select(x=>x+2).AverageWindow(x, 5sec).Where(x=>x > 0.8) *
Where フィルターのしきい値条件を変更し、Select ステートメントを追加するか、データフロー グラフに別の分岐を追加して、新しい出力ストリームを生成します。 既存のシステムでは、トポロジ全体を破棄し、データフローを最初から再起動しないと、これを行うことができません。 実際には、これらのシステムで既存の計算のチェックポイントが作成され、最新のチェックポイントから再起動できるようになります。 それでも、このような再起動は中断を伴い、リアルタイムで結果を生成するオンライン サービスにコストがかかります。 このような再起動は、似てはいるが (ユーザーごと、デバイスごとなどに) 異なるパラメーターを使用して実行され、絶えず変化するそうした多くの式について話している場合は、特に非現実的になります。
新しいリンクやノードを計算グラフに追加したり、計算ノード内の処理ロジックを変更したりすることで、実行時にストリーム処理グラフを進化できるシステムが必要です。
きめ細かいストリームの細分性
既存のシステムでは、抽象化の最小単位は通常、フロー全体 (トポロジ) です。 ただし、ターゲット シナリオの多くは、トポロジ内の個々のノード/リンクがそれ自体で論理エンティティである必要があります。 そうすることで、各エンティティを独立して管理できる可能性があります。 たとえば、複数のリンクを含むビッグ ストリーム トポロジでは、異なるリンクが異なる特性を持つことができ、異なる物理トランスポートに実装できます。 TCP ソケットを経由するリンクもあれば、リライアブル キューを経由するリンクもあります。 リンクによって配送保証が異なる場合があります。 ノードごとに異なるチェックポイント戦略を持つことができ、処理ロジックは、異なるモデルや、異なる言語でも表現できます。 このような柔軟性は、通常、既存のシステムでは不可能です。
抽象化の単位と柔軟性に関する議論は、SoA (サービス指向アーキテクチャ) とアクターの比較に似ています。 アクター システムでは、各アクターが基本的に独立して管理される "小さなサービス" であるため、柔軟性を向上できます。 同様に、このようなきめ細かい制御を可能にするストリーム システムが必要です。
Distribution
そしてもちろん、システムは "優れた分散システム" のすべての特性を備えている必要があります。 これには、以下が含まれます。
- "スケーラビリティ" - 多数のストリームとコンピューティング要素をサポートする
- "弾力性" - 負荷に基づいてリソースを追加/削除し、拡大/縮小できる
- "信頼性" - 障害に対する回復性があること
- "効率性" - 基になるリソースを効率的に使用する
- "応答性" - ほぼリアルタイムのシナリオを可能にする
これらは、Orleans ストリーミングを構築するために念頭に置いた要件でした。
"明確化": Orleans では、現在、上記の例のような宣言型データフロー式の記述は直接サポートされていません。 現在の Orleans ストリーミング API は、こちらで説明されているように、より低レベルの構成要素です。 宣言型データフロー式の提供は、今後の目標です。
関連項目
.NET