3

グローバル イベント エミッターを使用して Node.js アプリケーションを作成しています。つまり、私のアプリケーションは完全にイベントを中心に構築されています。この種のアーキテクチャは、ここで説明する 1 つの副次的なケースを除いて、私にとって非常にうまく機能していると思います。

この質問に答えるのに Node.js の知識は必要ないと思います。したがって、私はそれを抽象化しようとします。

次の状況を想像してください。

  • グローバル イベント エミッタ ( と呼ばれるmediator) を使用すると、個々のモジュールがアプリケーション全体のイベントをリッスンできます。
  • 着信要求を受け入れる HTTP サーバーが作成されます。
  • 着信リクエストごとに、このリクエストに固有のイベントを処理するイベント エミッターが作成されます。

着信リクエストの例 (純粋にこの質問を説明するため):

mediator.on('http.request', request, response, emitter) {

    //deal with the new request here, e.g.:
    response.send("Hello World.");

});

ここまでは順調ですね。要求された URL を識別し、適切なイベントを発行することで、このアプリケーションを拡張できます。

mediator.on('http.request', request, response, emitter) {

    //identify the requested URL
    if (request.url === '/') {
        emitter.emit('root');
    }
    else {
        emitter.emit('404');
    }

});

これに続いて、ルート要求を処理するモジュールを作成できます。

mediator.on('http.request', function(request, response, emitter) {

    //when root is requested
    emitter.once('root', function() {

        response.send('Welcome to the frontpage.');

    });

});

いいですね。実際には、コードが壊れている可能性があります。その理由は、行emitter.emit('root')が行の前に実行される可能性があるためですemitter.once('root', ...)。その結果、リスナーが実行されることはありません。

rootイベントの発行をイベント ループの最後まで遅らせることで、この特定の状況に対処できます。

mediator.on('http.request', request, response, emitter) {

    //identify the requested URL
    if (request.url === '/') {
        process.nextTick(function() {
            emitter.emit('root');
        });
    }
    else {
        process.nextTick(function() {
            emitter.emit('404');
        });
    }

});

これが機能する理由は、現在のイベント ループが終了するまでエミッションが遅延され、すべてのリスナーが登録されるためです。

ただし、このアプローチには多くの問題があります。

  1. このようなイベント ベースのアーキテクチャの利点の 1 つは、発行モジュールが、誰がイベントをリッスンしているかを知る必要がないことです。したがって、イベントの発行を遅らせる必要があるかどうかを判断する必要はありません。なぜなら、何がイベントをリッスンするのか、それを遅らせる必要があるかどうかがわからないからです。
  2. コードが大幅に乱雑になり、複雑になります (2 つの例を比較してください)。
  3. おそらくパフォーマンスが低下します

結果として、私の質問は次のとおりです。説明した状況のように、イベントの発行をイベントループの次のティックに遅らせる必要をどのように回避しますか?

2013 年 1 月 19 日更新

この動作が役立つ理由を示す例: http 要求を並行して処理できるようにするため。

mediator.on('http.request', function(req, res) {

    req.onceall('json.parsed', 'validated', 'methodoverridden', 'authenticated', function() {
        //the request has now been validated, parsed as JSON, the kind of HTTP method has been overridden when requested to and it has been authenticated
   });

});

各イベントjson.parsedが元のリクエストを発行する場合、各イベントは別のリクエストに関連しており、特定のリクエストに対して並行して実行されるアクションの組み合わせをリッスンできないため、上記は不可能です。

4

4 に答える 4

3

mediatorイベントをリッスンする と、イベントをリッスンしてトリガーする の両方を持つことは、emitter非常に複雑に思えます。正当な理由があると確信していますが、私の提案は単純化することです。eventBus同様のことを行う nodejs サービスでグローバルを使用します。この状況では、新しいイベントを発行します。

bus.on('http:request', function(req, res) {
  if (req.url === '/')
    bus.emit('ns:root', req, res);
  else
    bus.emit('404');
});

// note the use of namespace here to target specific subsystem
bus.once('ns:root', function(req, res) {
  res.send('Welcome to the frontpage.');
});
于 2012-11-17T04:21:09.373 に答える
1

オブザーバーパターンのいくつかの欠点に遭遇し始めているようです(このパターンを説明する多くの本/記事で言及されているように)。私の解決策は理想的ではありません–理想的な解決策が存在すると仮定します–しかし:

イベントがエミッターごとに1回だけ発行される(つまり、インスタンスemitter.emit('root');に対して1回だけ呼び出されるemitter)という単純な仮定を立てることができれば、jQueryの$.ready()イベントのように機能するものを記述できます。

その場合、サブスクライブすると、すでに発行されているemitter.once('root', function() { ... })かどうかがチェックされ、発行されている場合'root'は、とにかくハンドラーが呼び出されます。また、'root'まだ発行されていない場合は、通常の既存の機能に従います。

私が得たのはそれだけです。

于 2012-11-15T20:00:47.747 に答える
1

アクションの明確な順序を必要とする順次作業 (I/O) を行っているが、非決定的な実行順序を自然に許可するコンポーネント上にアプリを構築することを計画しているため、このアーキテクチャは問題があると思います。

できること

たとえば、この方法で mediator.on 関数にコンテキストセレクターを含めます

mediator.on('http.request > root', function( .. ) { } )

またはそれをサブメディエーターとして定義します

var submediator = mediator.yield('http.request > root');
submediator.on(function( ... ) {
     emitter.once('root', ... )
}); 

これは、root が http.request ハンドラーから発行された場合にのみコールバックをトリガーします。

もう 1 つのトリッキーな方法は、バックグラウンドで順序付けを行うことですが、現在の 1 つのメディエーターがすべてのインターフェイスを支配しているとは言えません。各 .emit 呼び出しが実際にイベントを送信するのではなく、生成されたイベントをリストに入れるようにコードを実装します。各 .once は、消費イベント レコードを同じリストに入れます。すべての mediator.on コールバックが実行されたら、リストをウォークスルーし、依存関係の順序で並べ替えます (たとえば、リストが最初に「ルート」を消費し、次に「ルート」を生成する場合、それらを交換します)。次に、消費ハンドラを順番に実行します。イベントがなくなったら、実行を停止します。

于 2012-11-16T14:33:52.990 に答える
0

Oi、これはいくつかの理由で非常に壊れたアーキテクチャのようです。

  1. どのように通り過ぎますrequestresponse?それらへのグローバル参照があるようです。
  2. 私があなたの質問に答えると、あなたのサーバーは純粋な同期関数になり、async node.js の力を失うことになります。(リクエストは効果的にキューに入れられ、最後のリクエストが 100% 終了して初めて実行を開始できます。)

これを修正するには:

  1. request&responseemit()パラメーターとして呼び出しに渡します。次のコンポーネントがイベントを処理するときに、正しい要求と応答オブジェクトへの参照を持つようになるため、すべてを強制的に同期的に実行する必要がなくなりました。
  2. グローバル メディエータを必要としないその他の一般的なソリューションについて学びます。Connect が何年も前にインターネットに基づいていたパターンを見てください: http://howtonode.org/connect-it <- ミドルウェア/オニオン ルーティングについて説明します
于 2012-11-07T15:14:08.930 に答える