【问题标题】:Workload balancing between akka actorsakka actor 之间的工作负载平衡
【发布时间】:2015-08-16 19:51:52
【问题描述】:

我有 2 个用于爬取链接的 akka 演员,即找到页面 X 中的所有链接,然后找到从 X 链接的所有页面中的所有链接,等等......

我希望他们或多或少地以相同的速度进步,但往往他们中的一个会挨饿,而另一个会消耗所有资源。

我尝试了以下方法(简化)。 单页抓取由以下参与者完成:

class Crawler extends Actor {
  def receive = {
    case Crawl(url, kind) =>
      // download url
      // extract links
      sender ! Parsed(url, links, kind)
  }
}

方法一:

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)
      }
  }
}

方法2:

class Coordinator extends Actor {
  val linksA = ...
  val linksB = ...
  val rrProps = Props[Crawler].withRouter(RoundRobinRouter(nrOfInstances = 10)
  val crawlerA = context.actorOf(rrProps)
  val crawlerB = context.actorOf(rrProps)
  def receive = {
    case Parsed(url, links, kind) =>
      val store = if (kind == kindA) linksA else linksB
      val newLinks = links -- store
      store ++= links
      newLinks.foreach { link =>
        if (kind == kindA) crawlerA ! Crawl(link, kind)
        else crawlerB ! Crawl(link, kind)
      }
  }
}

第二种方法使事情稍微好一些,但并没有完全解决。

有没有一种好方法可以让两种爬虫或多或少地以相同的速度前进?我是否应该在它们之间发送消息以依次解除阻塞?

【问题讨论】:

    标签: multithreading scala web-crawler akka actor


    【解决方案1】:

    我正在开发一个类似的程序,其中工作人员的资源成本不统一(在我的情况下,任务是执行数据库查询并将结果转储到另一个数据库中,但就像抓取不同的网站会有不同的成本一样,所以不同的查询也会有不同的成本)。我采用了两种处理方法:

    1. RoundRobinRouter 替换为SmallestMailboxRouter
    2. 不要让Coordinator 一次发送所有消息 - 而是分批发送它们,在您的情况下,您有 10 个工作人员,因此发送 40 条消息应该让他们一开始就很忙。每当工作人员完成一项任务时,它都会向Coordinator 发送一条消息,此时Coordinator 会发送另一条消息,该消息可能会发送给刚刚完成其任务的工作人员。 (您也可以分批执行此操作,即在收到n“任务完成”消息后,Coordinator 会发送另一个n 消息,但不要将n 设置得太高,否则一些工作人员的任务非常短可能是空闲的。)

    第三种选择是欺骗并在所有参与者之间共享ConcurrentLinkedQueue:填满队列后,Coordinator 向工作人员发送“开始”消息,然后工作人员轮询队列直到它为空。

    【讨论】:

    • 谢谢。这两种方法似乎都很有趣,尤其是SmallestMailboxRouter。我会试试看。
    【解决方案2】:

    我也会考虑使用 Work Pulling 模式。在这里很好地描述了它,例如:"Akka Work Pulling Pattern to prevent mailbox overflow, throttle and distribute work"。 Zim-Zam 回答的方式#2 基本上是相同的方法。

    因此,当新的已解析链接可用时,您无需立即将所有任务“推送”到您的爬虫,而是宣布这些内容可用,然后它们将在准备好时“拉取”该工作。

    此外,在协调器方面,如果需要,您可以添加一些更复杂的逻辑来确定某些链接类型工作的优先级(例如,使用 PriorityQueue 处理待处理的链接)。

    使用SmallestMailboxRouter 确实会让事情稍微好一点,但本质上它仍然是基于“推动”的方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-09-05
      • 1970-01-01
      • 2016-07-08
      • 2013-09-25
      • 2015-09-26
      • 2017-11-13
      • 1970-01-01
      相关资源
      最近更新 更多