3

ZeroMQ の学習を始めたばかりで、学習しながら例として分散 Web クローラーを構築したいと考えています。

私の考えは、クロールを開始する必要がある URL を受け入れる、PHP で記述された「サーバー」を用意することです。

ワーカー (C# cli) は、その URL をクロールし、リンクを抽出して、サーバー上のスタックにプッシュする必要があります。サーバーは、スタック内の URL をワーカーに送信し続けます。おそらく、redis はクロールされたすべての URL を追跡するため、サイトを複数回クロールすることはなく、現在のプロセスの統計を抽出することができます。

サーバーにタスクを均等に分散させ、新しい/欠落しているワーカーを認識させ、ワーカーが応答しない場合に URL を再分散させたいと考えています。

サーバーに PHP を使用する理由:私は PHP に非常に慣れています。それだけです。サンプル/テスト プロジェクトをより複雑にしたくありません。

ミニオンに C# を使用する理由:ほとんどの Windows マシンで実行されるからです。実行可能ファイルをさまざまな友人に渡して、実行してもらい、プロジェクトのテストを手伝ってもらうことができます。

クロール プロセスと redis 機能は私の質問の一部ではありません。

私の最初のアプローチは PUSH/PULL パターンでした。これは通常、私のシナリオでは機能しますが、それがミニオンであることを認識していません。真ん中に DEALER/ROUTER ブローカーが必要で、ワーカーの認識を自分で処理する必要があると思います。

この質問を見つけましたが、答えを理解しているかどうかはよくわかりません...

zmq を実装する方法のヒントを求めています。ディーラーのアプローチは正しいですか?従業員を自動的に認識させる方法はありますか? いくつかのリソース/例が必要だと思いますか、それとも zmq ガイドをさらに深く掘り下げる必要があると思いますか?

ただし、正しい方向へのいくつかのヒントは素晴らしいでしょう:)

乾杯

4

1 に答える 1

7

原則として、少なくともクローラーと同じように機能するジョブ/タスク ディストリビューターを構築しています。ここに私が学んだいくつかのことがあります:

すべてのイベントを定義

サーバーとクローラー間の通信は、サーバーからクローラーへの作業のディスパッチや、サーバーへのハートビート メッセージの送信など、システムで発生するさまざまな事柄に基づいています。システムのイベント タイプを定義します。それらはユースケースです:

DISPATCH_WORK_TO_CRAWLER_EVENT
CRAWLER_NODE_STATUS_EVENT
...

メッセージ標準を定義する

サーバーとクローラー間のすべての通信は ZMsg を使用して行う必要があるため、次のようなフレームを編成する標準を定義します。

Frame1: "Crawler v1.0"             //this is a static header
Frame2: <event type>               //ex: "CRAWLER_NODE_STATUS_EVENT"
Frame3: <content xml/json/binary>  //content that applies to this event (if any)

すべてのメッセージが従わなければならない標準的な規則があるため、ピア間で受信した ZMsgs を検証するメッセージ バリデーターを作成できるようになりました。

サーバ

ROUTERクローラーとの非同期および双方向通信には、サーバー上で単一の を使用します。また、PUBハートビート メッセージのブロードキャストにソケットを使用します。

ROUTER ソケットでブロックしないでください。a を使用しPOLLERて 5 秒ごとにループするなどしてください。これにより、サーバーはハートビート イベントをクローラーにブロードキャストするなど、他のことを定期的に実行できます。このようなもの:

Socket rtr = .. //ZMQ.ROUTER
Socket pub = .. //ZMQ.PUB  
ZMQ.Poller poller = new ZMQ.Poller(2)
poller.register( rtr, ZMQ.Poller.POLLIN)                               
poller.register( pub, ZMQ.Poller.POLLIN)

  while (true) {
     ZMsg msg = null            
     poller.poll(5000)

     if( poller.pollin(0)){
        //messages from crawlers                         
        msg = ZMsg.recvMsg(rtr)
     }

     //send heartbeat messages
     ZMsg hearbeatMsg = ...
     //create message content here,
     //publish to all crawlers
     heartbeatMsg.send(pub)
  }

ワーカーの認識に関する質問に答えるには、FIFO スタックとハートビート メッセージを使用する簡単で効果的な方法があります。このようなもの:

  • サーバーは単純な FIFO スタックをメモリに保持します
  • サーバーはハートビートを送信します。クローラーはノード名で応答します。ROUTER は自動的にノードのアドレスもメッセージに挿入します (メッセージのエンベロープを参照してください) 。
  • ノード名とノード アドレスを含むスタックに 1 つのオブジェクトをプッシュします。
  • サーバーが作業をクローラーにディスパッチしたい場合は、スタックから次のオブジェクトをポップし、メッセージを作成し、アドレスを (ノード アドレスを使用して) 適切に作成し、そのワーカーに送信します。
  • 同じ方法で他のクローラーにさらに作業をディスパッチします。クローラーがサーバーに応答したら、ノード名/アドレスを持つ別のオブジェクトをスタックにプッシュします。他のワーカーは応答するまで利用できないため、気にする必要はありません。

これは、やみくもに作業を送信するのではなく、ワーカーの可用性に基づいて作業を分散するための単純ですが効果的な方法です。lbbroker.phpの例を確認してください。概念は同じです。

クローラー (ワーカー)

ワーカーは、単一のDEALERソケットとSUB. はDEALER非同期通信のメイン ソケットであり、SUB はサーバーからのハートビート メッセージをサブスクライブします。ワーカーは、ハートビート メッセージを受信すると、DEALER ソケットでサーバーに応答します。

Socket dlr = .. //ZMQ.DEALER
Socket sub = .. //ZMQ.SUB
ZMQ.Poller poller = new ZMQ.Poller(2)
poller.register( dlr, ZMQ.Poller.POLLIN)                               
poller.register( sub, ZMQ.Poller.POLLIN)

  while (true) {
     ZMsg msg = null            
     poller.poll(5000)

     if( poller.pollin(0)){
        //message from server                         
        msg = ZMsg.recvMsg(dlr)
     }

     if( poller.pollin(1)){
      //heartbeat message from server
       msg = ZMsg.recvMsg(sub)
       //reply back with status
       ZMsg statusMsg = ...
       statusMsg.send(dlr)
  }

残りは自分で把握できます。PHP の例に取り組み、何かを構築し、それを壊し、さらに構築してください。それがあなたが学ぶ唯一の方法です!

楽しんでください、お役に立てば幸いです!

于 2013-10-17T02:09:06.753 に答える