イベント駆動型アーキテクチャで初期状態を処理する方法は?


33

イベント駆動型アーキテクチャイベントがシステムを介して送信されたときに各コンポーネントにのみ作用します。

ブレーキペダルとブレーキライトを備えた架空の車を想像してください。

  • ブレーキライトターンそれが受信したときbrake_onのイベントを、そしてオフそれが受信したときbrake_offのイベント。
  • ブレーキペダルは、送信brake_onのそれが押下されたイベント、およびbrake_offの、それが解放されるイベントを。

これは、ブレーキペダルが既に踏み込まれた状態で車の電源が入る状況になるまで、すべてうまくいきます。ブレーキライトはbrake_onイベントを受け取ったことがないため、消灯したままになります。これは明らかに望ましくない状況です。デフォルトでブレーキライトをオンにすると、状況が逆転するだけです。

この「初期状態の問題」を解決するために何ができますか?

編集:すべての応答をありがとう。私の質問は実際の車に関するものではありませんでした。車では、状態を継続的に送信することでこの問題を解決しました。したがって、そのドメインにスタートアップの問題はありません。私のソフトウェアドメインでは、そのソリューションは多くの不必要なCPUサイクルを使用します。

編集2:@gbjbaanbの答えに加えて、私は次のようなシステムに行きます:

  • 架空のブレーキペダルは、初期化後、その状態でイベントを送信し、
  • 仮想ブレーキライトは、初期化後、ブレーキペダルからの状態イベントを要求するイベントを送信します。

このソリューションでは、コンポーネント間の依存関係、競合状態、失効するメッセージキュー、および「マスター」コンポーネントはありません。


2
最初に頭に浮かぶのはinitialize、必要なセンサーデータを含む「合成」イベント(それを呼び出す)を生成することです。
msw

ペダルはbrake_pedal_onイベントを送信し、実際のブレーキはbrake_onイベントを送信するべきではないでしょうか?ブレーキが機能していない場合、ブレーキライトが点灯しないようにします。
bdsl

3
これは仮説的な例だと言ったことがありますか?:-)質問を簡潔かつ要点を保つために大幅に簡素化されています。
フランクKusters

回答:


32

これを行うには多くの方法がありますが、メッセージベースのシステムを可能な限り分離することをお勧めします。これは、システム全体がコンポーネントの状態を読み取ることも、コンポーネントが他のコンポーネントの状態を読み取ることもできないことを意味します(そのように依存関係のスパゲッティ関係があるため)。

そのため、実行中のシステムはそれ自体の世話をしますが、各コンポーネントに起動するように指示する方法が必要です。また、コンポーネントの登録にはそのようなことが既にあります。登録されました(または、各コンポーネントに詳細を返して登録できるように要求します)。これは、コンポーネントが起動タスクを実行できる段階であり、通常の操作と同じようにメッセージを送信できます。

したがって、ブレーキペダルは、イグニッションが開始されると、車の管理から登録/確認メッセージを受信し、「私はここにいます」というメッセージだけでなく、独自の状態を確認して、その状態のメッセージ(たとえば、ペダルを踏み込んだメッセージ)。

ブレーキライトがまだ登録されていない場合、メッセージは受信されないように、問題はスタートアップの依存関係の1つになりますが、コアシステムがスタートアップ、登録、チェックルーチンを完了するまでこれらのメッセージをすべてキューに入れることで簡単に解決できます。

最大の利点は、初期化を処理するために特別なコードが必要ないことです。ただし、既に記述している必要があります(OK、ブレーキペダルイベントのメッセージ送信がブレーキペダルハンドラーにある場合は、初期化でも呼び出す必要があります) 、ただし、ハンドラーロジックに強く結び付けられたコードを記述していない限り、通常は問題になりません。また、コンポーネントが通常どおりに相互に既に送信しているコンポーネントを除き、コンポーネント間の相互作用はありません。このため、メッセージパッシングアーキテクチャは非常に優れています。


1
私はあなたの答えが好きです。それはすべてのコンポーネントを分離したままにするからです。このアーキテクチャを選択する最も重要な理由でした。ただし、現在、システムが「初期化」状態にあると判断する実際の「マスター」コンポーネントはありません。すべてが実行を開始します。結果として私の質問に問題がある。マスターがシステムの実行を決定すると、「システム初期化」イベントをすべてのコンポーネントに送信できます。その後、すべてのコンポーネントがその状態のブロードキャストを開始します。問題が解決しました。ありがとうございました!(今、システムが初期化されているかどうかを判断する方法に問題が残っています...)
フランククスター

ステータス更新ディスパッチャに各オブジェクトから受信した最新の更新を追跡させ、新しいサブスクリプション要求を受信するたびに、登録されたイベントソースから受信した最新の更新を新しいサブスクライバーに送信しますか?
supercat

その場合、イベントの有効期限を追跡する必要もあります。すべてのイベントが、登録される可能性のある新しいコンポーネントを永久に保持できるわけではありません。
フランクKusters

@spaceknarfよく、「すべてがちょうど実行を開始する」場合は、コンポーネントに依存関係を構築できないため、ライトの後にペダルが開始されます。それらを「正しい」順序で並べます(たとえば、最初に起動するサービスが1.xxx、2番目が2.xxxなどと呼ばれるsystemdの前のLinux起動initスクリプト)。
gbjbaanb

そのような順序のスクリプトは脆弱です。多くの暗黙的な依存関係が含まれています。代わりに、実行するコンポーネントの静的に構成されたリスト(@Lie Ryanによる)を持つ「マスター」コンポーネントがあり、すべてのコンポーネントがロードされると「準備完了」イベントをブロードキャストできるかどうかを考えていました。それに応じて、すべてのコンポーネントは初期状態をブロードキャストします。
フランクKusters

4

ロード/起動時に状態を適切に設定する初期化イベントを設定できます。これは、複数のハードウェアを含まない単純なシステムまたはプログラムにとって望ましい場合がありますが、複数の物理コンポーネントを備えたより複雑なシステムでは、初期化をまったく行わないのと同じリスクを伴います。システム(CANベースのシステムなど)では、ブレーキを押した状態でシステムを開始したかのように、システムを誤って後方に設定してしまう可能性があります。車の場合のように、コントローラーが多いほど、何かを見逃す可能性が高くなります。

これを説明するために、「ブレーキオン」ロジックに「ブレーキオン」イベントを繰り返し送信させることができます。おそらく1/100秒ごとか何か。脳を含むコードは、これらのイベントをリッスンし、それらを受信して​​いる間に「ブレーキオン」をトリガーできます。「ブレーキオン」信号を受信しない1/10秒後に、内部の「brake_off」イベントをトリガーします。

異なるイベントには、かなり異なるタイミング要件があります。車では、ブレーキライトは、燃料チェックライト(数秒の遅延がおそらく許容される)やその他の重要度の低いシステムよりもはるかに高速である必要があります。

物理システムの複雑さにより、これらのアプローチのどちらがより適切であるかが決まります。あなたの例が乗り物だとすると、おそらく後者に似たものが欲しいでしょう。

いずれにせよ、物理システムでは、正しく受信/処理される単一のイベントに依存することは望ましくありません。ネットワーク接続されたシステム上の接続されたマイクロコントローラーには、この理由で「I'm alive」タイムアウトがよくあります。


物理システムでは、ワイヤを実行してバイナリロジックを使用します。HIGHはブレーキが押され、LOWはブレーキが押されていない
ラチェットフリーク

@ratchetfreakは、この種のことについて多くの可能性があります。おそらくスイッチがそれを処理できます。単純に処理されないシステムイベントは他にもたくさんあります。
エンダーランド

1

この場合、ブレーキを単純なオン/オフとしてモデル化しません。むしろ、「ブレーキ圧力」イベントを送信します。たとえば、圧力0はオフを示し、圧力100は完全に押し下げられます。システム(ノード)は、必要に応じて(一定の間隔で)コントローラーに破壊圧力イベントを絶えず送信します。

システムが起動されると、オフになるまで圧力イベントの受信を開始します。


1

状態情報を渡す唯一の手段がイベントによるものである場合、問題が発生しています。代わりに、次の両方ができる必要があります。

  1. ブレーキペダルの現在の状態を照会します。
  2. ブレーキペダルからの「状態変更」イベントに登録します。

ブレーキライトは、ブレーキペダルの観察者として見ることができます。言い換えると、ブレーキペダルはブレーキライトについて何も知らず、ブレーキライトなしでも操作できます。(これは、「初期状態」イベントを積極的にブレーキライトに送信するブレーキペダルの概念は、よく考えられていないことを意味します。)

システムがインスタンス化されると、ブレーキライトはブレーキペダルに登録してブレーキ通知を受信し、ブレーキペダルの現在の状態を読み取り、オンまたはオフにします。

次に、3つの方法のいずれかでブレーキ通知を実装できます。

  1. パラメータのない「ブレーキペダルの状態が変化した」イベントとして
  2. 「ブレーキペダルが現在押されている」イベントと「ブレーキペダルが離されている」イベントのペアとして
  3. 「押し下げられた」または「解放された」パラメータを持つ「新しいブレーキペダル状態」イベントとして。

私は最初のアプローチを好みます。つまり、通知を受け取ると、ブレーキランプは既に知っている方法を実行します。つまり、ブレーキペダルの現在の状態を読み取り、それ自体をオンまたはオフにします。


0

イベントドリブンシステム(私は現在使用しており、これが大好きです)では、できる限り分離しておくことが重要です。その考えを念頭に置いて、すぐに掘り下げましょう。

いくつかのデフォルト状態を持つことが重要です。ブレーキライトはデフォルト状態の「オフ」になり、ブレーキペダルはデフォルト状態の「アップ」になります。その後の変更はイベントになります。

次に、質問に対処します。ブレーキペダルが初期化されて押し下げられ、イベントが発生したが、イベントを受信するためのブレーキライトがまだないことを想像してください。ロジックを初期化するに、オブジェクトの作成(イベントリスナーが初期化される場所)を個別のステップとして分離するのが最も簡単であることがわかりました。それはあなたが説明したような競合状態を防ぎます。

また、事実上同じものに 2つの異なるイベントを使用するのは厄介ですbrake_offそして、パラメータでbrake_on簡略化できe_brakeますbool on。サポートデータを追加することにより、この方法でイベントを簡素化できます。


0

必要なのは、ブロードキャストイベントとメッセージ受信ボックスです。ブロードキャストは、不特定数のリスナーに公開されるメッセージです。コンポーネントは、ブロードキャストイベントをサブスクライブできるため、関心のあるイベントのみを受信します。これにより、送信者は受信者が誰であるかを知る必要がないため、分離が実現します。サブスクリプションテーブルは、コンポーネントのインストール中に(初期化されるときではなく)静的に構成する必要があります。受信ボックスは、宛先コンポーネントがオフラインのときにメッセージを保持するバッファーとして機能するメッセージルーターの一部です。

請求書を使用すると、1つの問題が発生します。これは、受信トレイのサイズです。これ以上オンラインにならないコンポーネントのメッセージをシステムが保持し続ける必要はありません。これは、特にメモリの制約が厳しい組み込みシステムでは重要です。受信ボックスのサイズ制限を克服するには、すべてのブロードキャストメッセージがいくつかのルールに従う必要があります。ルールは次のとおりです。

  1. すべてのブロードキャストイベントには名前が必要です
  2. ブロードキャストイベントの送信者は、指定された名前のアクティブなブロードキャストを1つだけ持つことができます
  3. イベントによって引き起こされる効果はi等でなければなりません

ブロードキャスト名は、コンポーネントのインストール時に宣言する必要があります。コンポーネントが同じ名前で2番目のブロードキャストを送信してから、受信者が前のブロードキャストを処理する場合、新しいブロードキャストは以前のブロードキャストを上書きします。静的な受信ボックスのサイズ制限を設定できるようになりました。これにより、特定のサイズを超えないことが保証され、サブスクリプションテーブルに基づいて事前に計算できます。

最後に、ブロードキャストアーカイブも必要です。ブロードキャストアーカイブは、各ブロードキャスト名の最後のイベントを保持するテーブルです。インストールされたばかりの新しいコンポーネントには、ブロードキャストアーカイブからのメッセージが受信ボックスに事前入力されます。メッセージの受信トレイと同様に、ブロードキャストアーカイブのサイズも静的にできます。

さらに、メッセージルーター自体がオフラインになっている状況に対処するには、メッセージ送信トレイも必要です。メッセージ送信ボックスは、送信メッセージを一時的に保持するコンポーネントの一部です。

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