【问题标题】:Trying to build a distributed crawler with ZeroMQ尝试使用 ZeroMQ 构建分布式爬虫
【发布时间】:2013-10-24 15:52:13
【问题描述】:

刚开始学习ZeroMQ,想在学习的时候以搭建一个分布式网络爬虫为例。

我的想法是有一个用 PHP 编写的“服务器”,它接受一个应该开始抓取的 url。

Workers (C# cli) 必须抓取该 url,提取链接,并将它们推回服务器上的堆栈中。服务器不断将堆栈中的 url 发送给工作人员。 也许一个redis会跟踪所有爬取的url,所以我们不会多次爬取网站,并且有能力提取当前进程的统计信息。

我想让服务器平均分配任务,注意新的/失踪的工人,并在工人没有响应时重新分配网址。

为什么 PHP 用于服务器: 我对 PHP 非常满意,仅此而已。我不想让示例/测试项目更复杂。

为什么 C# 用于 minions: 因为它在大多数 Windows 机器上运行。我可以将可执行文件提供给可以执行它并帮助我测试我的项目的各种朋友。

爬取过程和redis功能不属于我的问题。

我的第一种方法是推/拉模式,它通常适用于我的场景,但不知道它是奴才。我认为我需要一个中间的 DEALER/ROUTER 经纪人,并且必须自己处理工人意识。

我找到了this question,但我不确定我是否理解答案...

我正在寻求一些提示如何实现 zmq 的东西。经销商的做法是否正确?有没有办法让员工自动意识?我想我需要一些资源/示例,还是您认为我只需要深入了解 zmq 指南?

但是,一些正确方向的提示会很棒:)

干杯

【问题讨论】:

  • 你看过 AsyncSvr 的例子了吗?
  • 我一定跳过了那个,但它看起来很有希望。稍后我会更深入地研究它。谢谢老哥!

标签: c# php zeromq


【解决方案1】:

我正在构建一个工作/任务分配器,它至少在原则上与你的爬虫一样工作。以下是我学到的一些东西:

定义所有事件

服务器和爬虫之间的通信将基于系统中发生的不同事情,例如从服务器分派工作到爬虫,或者爬虫向服务器发送心跳消息。定义系统的事件类型;它们是用例:

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)

现在您可以创建消息验证器来验证对等方之间收到的 ZMsg,因为您有一个所有消息都必须遵循的标准约定。

服务器

在服务器上使用单个ROUTER 与爬虫进行异步和双向通信。此外,使用PUB 套接字来广播心跳消息。

不要阻塞 ROUTER 套接字,使用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 也会自动将节点的地址放入消息中(阅读消息enveloping
  • 将 1 个对象推送到包含节点名称和节点地址的堆栈上
  • 当服务器想要将工作分派给爬虫时,只需从堆栈中弹出下一个对象,创建消息和正确的地址(使用节点地址),然后将其交给该工作人员
  • 以同样的方式将更多工作分派给其他爬虫;当爬虫响应服务器时,只需将另一个具有节点名称/地址的对象推回堆栈;其他工作人员在他们做出回应之前将无法使用,因此我们不会打扰他们。

这是一种基于工人可用性分配工作的简单但有效的方法,而不是盲目地派出工作。查看lbbroker.php的例子,概念是一样的。

爬虫(工人)

worker 应该使用单个 DEALER 套接字和 SUBDEALER 是异步通信的主套接字,SUB 订阅来自服务器的心跳消息。当worker收到心跳消息时,它会在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 示例,构建东西,破坏它,构建更多东西,这是您学习的唯一方法!

玩得开心,希望对你有帮助!

【讨论】:

  • 这非常有帮助。非常感谢你的朋友!
  • 非常好的和令人鼓舞的答案。
猜你喜欢
  • 2019-03-23
  • 1970-01-01
  • 2019-10-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多