6

私の友人がインタビューでこの問題を尋ねられました。ここでこの問題について議論したいと思います

この問題の効率的な実装は何ですか?

私に思い浮かぶ簡単なアイデアは、通常の memqueue で、Memcache マシンを使用して複数のリクエストをスケーリングし、memcache から DB に書き込みを行うコンシューマー ジョブを実行します。2 番目の部分では、SQL クエリを実行して、一致するサブスクライバーのリストを見つけることができます。

問題:-

イベントはこのシステムに発行されます。各イベントは、C1、C2、… CN と呼ばれる固定数 (N) の文字列列を含むと考えることができます。したがって、各イベントは文字列の配列として渡すことができます (C1 は配列の 0 番目の要素、C2 は 1 番目など)。

M 人のサブスクライバーがいます – S1, … SM

各サブスクライバーは、関心のあるイベントのサブセットを指定する述語を登録します。各述語には、次のものを含めることができます。

Equality clause on columns, for example: (C1 == “US”)
Conjunctions of such clauses, example: 
    (C1 == “IN”) && (C2 == “home.php”) 
    (C1 == “IN”) && (C2 == “search.php”) && (C3 == “nytimes.com”)

(上記の例では、C1 はイベントの国コードを表し、C2 はサイトの Web ページを表し、C3 はリファラー コードを表します。)

すなわち。– 各述語は、いくつかの等価条件の結合です。述語には必ずしもすべての列の等価句があるとは限らないことに注意してください (つまり、述語は一部またはすべての列の値を気にしない場合があります)。(上記の例では、#a は列 C3、… CN を気にしません)。

着信イベントと登録済みサブスクライバーを照合できる Dispatcher を設計およびコーディングする必要があります。着信イベント レートの単位は 1 秒あたり 100 万です。加入者数は数千人です。したがって、このディスパッチャーは非常に効率的でなければなりません。平易な言葉で:

When the system boots, all the subscribers register their predicates to the dispatcher
After this events start coming to the dispatcher
For each event, the dispatcher has to emit the id of the matching subscribers.

インターフェイスの仕様に関しては、次のように大まかに記述できます (Java で)。

Class Dispatcher {

    public Dispatcher(int N /* number of columns in each event – fixed up front */);

    public void registerSubscriber( String subscriberId /* assume no conflicts */,
                                    String predicate /* predicate for this subscriberid */);

    public List<String> findMatchingIds(String[] event /* assume each event has N Strings */);

}

つまり、ディスパッチャーが構築され、次に一連の registerSubscriber 呼び出しが行われます。この後、引き続きメソッド findMatchingIds() を呼び出します。この演習の目標は、この関数を可能な限り効率的にすることです。

4

4 に答える 4

1

私の頭に浮かぶ解決策は次のとおりです。

各Cnについて、Cnの値をサブスクライブしたサブスクライバーの値からサブスクライバーのセットへのマッピングがあります。さらに、Cnごとに、Cnの値(「ANY」)を気にしないサブスクライバーのセットがあります。

イベントを受信すると、Cnのサブスクリプションが一致するすべてのサブスクライバーを検索し、サブスクライバーが0以上のセットを受信します。このセットに、このCnの「ANY」セットからこれらのサブスクライバーを追加します。

これをn<=Nごとに実行し、nセットのサブスクライバーを生成します。nセットすべての共通部分は、このイベントに一致するサブスクライバーのセットです。

Cnからサブスクライバーへのマッピングは、ツリーとして効率的に保存できます。これにより、k個の異なる値へのサブスクリプションがある場合、単一のCnのサブスクライバーを検索するための複雑さO(k)= log(k)が得られます。

したがって、n個の値の場合、複雑さはO(n、k)= n * log(k)になります。

nセットの交差はO(n、m)= n * log(m)でも実行できるため、合計で対数の複雑さが発生しますが、それほど悪くはありません。

于 2012-12-26T15:18:46.267 に答える
1

Hanno Binder が暗示したように、この問題は、サブスクリプションを前処理して効率的なルックアップ構造を取得できるように明確に設定されています。ハンノはルックアップは地図であるべきだと言う

(N, K) -> set of subscribers who specified K in field N     
(N, "") -> set of subscribers who omitted a predicate for field N

イベントが到着したら、該当するすべてのセットを調べて、それらの交差点を見つけます。検索に失敗すると、空のセットが返されます。ハッシュ テーブルは O(1) であり、このアプリケーションではツリーよりもおそらく高速であることを指摘するために、Hanno の優れた回答を要約しているだけです。一方、交差するツリーは、S が交差サイズである O(S + log N) より高速になります。したがって、それはセットの性質に依存します。

これは、前処理中に一度だけ作成された別のルックアップ構造です。マップをコンパイルすることから始めます

(N, K) -> unique token T (small integer)

0「don't care」を表す際立ったトークンもあります。

現在、すべての述語は、特定のイベント文字列キーまたは「ドント ケア」を表す、N 個のトークンを持つ正規表現のようなパターンと考えることができます。

事前に決定木を構築できるようになりました。このツリーは、パターンを認識するための決定論的有限オートマトン (DFA) と考えることができます。エッジは、「ドントケア」を含むトークンでラベル付けされます。一致するエッジが他にない場合は、ドント ケア エッジが使用されます。受け入れ状態には、それぞれのサブスクライバー セットが含まれます。

イベントの処理は、キーをトークン パターンに変換することから始まります。マップ エントリがないためにこれが失敗した場合、サブスクライバはありません。それ以外の場合は、パターンを DFA にフィードします。DFA がクラッシュせずにパターンを使用する場合、最終状態にはサブスクライバー セットが含まれます。これを返してください。

たとえば、マップは次のようになります。

(1, "IN") -> 1
(2, "home.php") -> 2
(2, "search.php") -> 3
(3, "nytimes.com") -> 4

N=4 の場合、DFA は次のようになります。

o --1--> o --2--> o --0--> o --0--> o
          \
            -3--> o --4--> o --0--> o

C1 などを気にしないサブスクライバーは存在しないため、開始状態にはドントケア遷移がないことに注意してください。C1 に「IN」がないイベントはクラッシュを引き起こし、null セットが適切に返されます。

サブスクライバーが数千人しかいないため、この DFA のサイズは妥当なはずです。

ここでの処理時間はもちろん O(N) であり、実際には非常に高速になる可能性があります。実際の速度を得るには、前処理で C の switch ステートメントのネストを生成してコンパイルすることができます。この方法では、少数のプロセッサーで 1 秒あたり数百万のイベントを実際に取得する可能性があります。

flex スキャナー ジェネレーターのような標準的なツールを使って、ほとんどの作業を行うように仕向けることさえできるかもしれません。

于 2013-01-04T05:42:19.057 に答える
1

これは設計上の問題だと思います。インタビュアーが動作するコードを探していたとは思いません。一般的な問題は Content based Publish Subscribeと呼ばれ、同じ分野の論文を検索すると、多くの結果が得られます。たとえば、この論文も

システムに必要なものはいくつかあります

1) 保存する必要があるサブスクリプションのデータストア: a) サブスクライバーのリストを保存する b) サブスクリプションのリストを保存する

2) サブスクリプションの要求とノード自体を認証する手段 a) サーバー - サブスクライバーは ssl を介して通信します。何千もの SSL 接続を処理するサーバーの場合 - 特に多数の接続がバーストで設定されている場合、これは CPU 集中型のタスクです。
b) すべての加入者ノードが同じ信頼できるネットワークにある場合、ssl は必要ありません。

3) プッシュ ベースのモデルとプル ベースのモデルのどちらが必要か:

a) サーバーは、一致したフィルターごとに、ノードごとに表示される最新のタイムスタンプを維持できます。イベントがフィルターに一致すると、サブスクライバーに通知を送信します。次に、クライアントにリクエストを送信させます。次に、サーバーは一致するイベントの送信を開始します。

b) サーバーはフィルタを一度に照合してクライアントに送信します。

(a) と (b) の違いは、(a) ではクライアント側でより多くの状態が維持されることです。サブスクライバー固有のロジックを後で簡単に拡張できます。(b) では、クライアントは愚かです。なんらかの理由でイベントを受け取りたくない場合、それを言う手段はありません。(たとえば、ネットワークの詰まり)。

4) イベントはサーバー側でどのようにメモリに保持されますか?

 a)The logical model here is table with columns of strings (C1..CN), and each new row added is a new event.
b)We could have A hash-table per column storing a tupple of  (timestamp, pointer to event structure). And each event is given a unique id. With different data-structures,we can come up with different schemes. 
 c) Events here are considered as infinite stream. If  we have a 32-bit eventId, we have chances of integer-overflow.
 d) If we have a timer function on  the server, matching and dispatching events,what is the actual resolution of the system timer? Does that have any implication? 
     e) Memory allocation is a very expensive operation.  If your filter-matching logic is going to do frequent allocations/ freeing, it will adversely affect performance.  How can we manage the memory-pool for this particular operation? Would we different size-buckets of  page-aligned memory? 

5) 加入者ノードが接続を失ったり、ダウンしたりした場合はどうすればよいですか? (a)クライアントが期間中にイベントを失うことは許容できますか?それともサーバーがすべてをバッファリングする必要がありますか? (b) サブスクライバがダウンした場合、一致するイベントをリクエストできるのは過去のどの時点までか。

6) (サーバー、サブスクライバー) 間のメッセージング層の詳細 (a) サーバーとサブスクライバー間の通信は同期ですか、それとも非同期ですか?
(b) クライアント/サーバー間にバイナリプロトコルまたはテキストベースのプロトコルが必要ですか? (どちらにもトレードオフがあります)

7) サーバー側にレート制限ロジックが必要ですか? いくつかのクライアントにデータを提供している間に一部のクライアントを飢えさせた場合、どうすればよいでしょうか?

8) サブスクリプションの変更はどのように管理されますか? 一部のクライアントがサブスクリプションを変更したい場合、永続的なデータストアを更新する前に、最初にメモリ内で更新する必要がありますか? それともその逆?データストアが書き込まれる前にサーバーがダウンした場合はどうなりますか? データ ストア (サブスクリプション/サーバー リスト) の一貫性を確保するにはどうすればよいでしょうか?

9) これは、単一のサーバーがあることを前提としていました。サブスクライバーが接続できるサーバーのクラスターが必要な場合はどうすればよいでしょうか? (ここにたくさんの問題があります: ) a) ネットワーク分割はどのように処理できますか? (例: 5 つのノードのうち、3 つのノードは相互に到達可能であり、他の 2 つのノードは他の 2 つのノードのみに到達可能ですか?) b) イベント/ワークロードはクラスターのメンバー間でどのように分散されますか?

10) サブスクライバーに送信される情報の絶対的な正確性は要件ですか?つまり、クライアントは、そのサブスクリプション ルールが示す追加情報を受信できますか? これにより、データ構造の選択を決定できます。たとえば、サーバー側でブルーム フィルターのような確率的データ構造を使用して、フィルタリングを行います。

11) イベントの時間順はサーバー側でどのように維持されますか? (時系列でソートされた連結リスト? タイムスタンプ?)

12) サブスクリプションの述語論理パーサーは Unicode サポートを必要としますか?

結論として、コンテンツベースの pub-sub はかなり広大な領域であり、データベース、ネットワーク、アルゴリズム、ノードの動作 (システムがダウンし、ディスクが故障し、システムがメモリ不足のためにメモリ不足になる) の相互作用を伴う分散システムです。メモリリークなど) - これらすべての側面を調べる必要があります。そして最も重要なことは、実際の実装に利用できる時間を調べて、この問題を解決する方法を決定する必要があることです。

于 2013-01-05T09:21:45.067 に答える
1

面白い。

私の最初の考え。サブスクライバがたとえば次のように述語すると簡単になると思います

(C1 == “IN”) && (C2 == “search.php”) && (C3 == “nytimes.com”)

ディスパッチャに来る

public void registerSubscriber

メソッドをフラット化して、比較のためにパフォーマンスを向上させる必要があります。以下のようなもの(勝手な推測)

C1IN|C2search.php|C3nytimes.com

次に、イベント文字列とサブスクライバー ID を使用してマップをメモリに保持する必要があります。

の中に

findMatchingIds

メソッド - イベントの String 配列も、一致するサブスクライバー ID を検索できるように、同様のルールでフラット化する必要があります。

このようにして、Dispatcher を水平方向にスケーリングし、多くのイベントを並行して処理できます

于 2013-01-03T15:35:25.610 に答える