我有2个用于抓取链接的akka演员,即查找第X页中的所有链接,然后查找从X等链接的所有页面中的所有链接...
我希望他们以相同的速度或多或少地前进,但往往其中一人变得饥饿而另一人消耗所有资源。
我尝试过以下方法(简化)。 单页抓取由以下actor完成:
$('.your-form').trigger($.extend($.Event('submit'), { parsley: true }))
方法1:
class Crawler extends Actor {
def receive = {
case Crawl(url, kind) =>
// download url
// extract links
sender ! Parsed(url, links, kind)
}
}
方法2:
class Coordinator extends Actor {
val linksA = ...
val linksB = ...
def receive = {
case Parsed(url, links, kind) =>
val store = if (kind == kindA) linksA else linksB
val newLinks = links -- store
store ++= links
newLinks.foreach { link =>
val crawler = context.actorOf(Props[Crawler])
crawler ! Crawl(link, kind)
}
}
}
第二种方法使事情稍好一些,但没有完全修复它。
是否有一种很好的方法可以使这两种爬行器以相同的速度或多或少地进步?我应该在它们之间发送消息吗?依次解锁对方?
答案 0 :(得分:2)
我正在开发一个类似的程序,其中工作人员的资源成本不均匀(在我的情况下,任务是执行数据库查询并将结果转储到另一个数据库中,但就像抓取不同的网站会有不同的成本所以不同的查询也会有不同的成本)。我采用的两种处理方式:
RoundRobinRouter
替换为SmallestMailboxRouter
Coordinator
一次发送所有邮件 - 而是将它们分批发送出去,如果你有十个工作人员,那么发送四十条邮件应该让他们最初保持忙碌。每当工作人员完成任务时,它就会向Coordinator
发送一条消息,此时Coordinator
会发送另一条消息,该消息可能会发送给刚刚完成其任务的工作人员。 (您也可以批量执行此操作,即在收到n
“任务完成”消息后,Coordinator
会发送另外的n
条消息,但不要使n
过高或者一些任务极短的工人可能会闲着。)第三种选择是欺骗并在所有参与者之间共享ConcurrentLinkedQueue
:在填充队列后,Coordinator
向工作人员发送“开始”消息,然后工作人员轮询队列直到它空。
答案 1 :(得分:1)
我还考虑使用工作拉动模式。这里很好地描述了它,例如:"Akka Work Pulling Pattern to prevent mailbox overflow, throttle and distribute work"。 来自Zim-Zam的方式#2基本上采用相同的方法。
因此,当新的已解析链接可用时,您可以立即宣布所有内容都可用,而不是立即将所有任务“推送”到您的抓取工具,然后他们会在准备就绪时“拉动”该工作。
此外,在协调员方面,您可以添加一些更复杂的逻辑,以便在需要时优先处理某些链接类型(例如,使用PriorityQueue
作为待处理链接)。
使用SmallestMailboxRouter
会使事情稍微更好,但实际上它仍然基于“推动”方法。