Scala / Akka拉模式 - 经理从工人那里获得更多工作

时间:2018-05-09 21:22:52

标签: scala design-patterns akka

我正在寻找我的爱好项目的帮助。我试图找到一个更好的方法,我的拉模式中的管理器将如何处理任务列表更新(需要完成的工作列表)

我已经实现了一个拉模式,其中有一个Job Factory,Manager和一个Worker。管理器在akka调度程序的帮助下从作业工厂获取新作业。每当有新工作时,工人就会得到通知并开始咀嚼。

在我的实现中可能是一个怪癖是,Worker会产生必须完成的新任务。目前,我已经通过从工作者到自身的递归消息解决了这个问题。

这是非常简单的想法:

class Worker extends Actor {
  def receive = {
    case Work0 ⇒ self ! Work1
    case Work1 ⇒ // ...
  }
}

但是,我不喜欢这种方法。我希望现有的任务列表在Manager端进行扩展,如果出现新任务,让工作人员将新的作业发送给Manager。

class Worker extends Actor {
  def receive = {
    case Work0 ⇒ Manager ! Work1
    case Work1 ⇒ // ...
  }
}

以下是如何在Manager内创建能够动态更改的工作缓冲区的解决方案。只要Worker将工作发送给Manager,它就会被添加到缓冲区之上。最终,Worker将遍历管理器在缓冲区内的所有任务。

这个例子没有拉模式本身的任何管道,这个想法只是为了测试如何让缓冲区工作。

import akka.actor.Actor
import akka.actor.ActorSystem
import akka.actor.Props
import scala.collection.mutable
import scala.collection.mutable.{ArrayBuffer, ListBuffer}

class Manager extends Actor {
  var iterator: Iterator[Int] = Iterator.empty
  var buffer: Option[mutable.ArrayBuffer[Int]] = None
  var iteratorCounter: Int = 0

  def receive = {
    case MyBuffer(workBuffer: mutable.ArrayBuffer[Int]) ⇒
      buffer = Some(workBuffer)
      iterator = workBuffer.iterator
    case "iterate" ⇒
      if (iterator.hasNext) {
        iterator.next() // This will be sent to worker as a Task to process
        iteratorCounter += 1
      } else if (buffer.get.length > iteratorCounter) {
        iterator = buffer.get.iterator
        iterator = iterator.drop(iteratorCounter)
      } else {
        iteratorCounter = 0
      }
    case "add" ⇒ // "Worker adds" new stuff to Manager
      val random = scala.util.Random
      val newVal = random.nextInt(100)
      val append = (buf: Option[ArrayBuffer[Int]], element: Int) => Some(buf.get += element)
      buffer = append(buffer, newVal)

    case _ => println("huh?")
  }
}

case class MyBuffer(workBuffer: ArrayBuffer[Int])
val buffer = mutable.ArrayBuffer(1,2,3,4,5,6,7,8,9,10)

val system = ActorSystem("PullPattern")
val helloActor = system.actorOf(Props(new Manager))
helloActor ! MyBuffer(buffer)
var a = 0

for (a <- 1 to 5) {
  helloActor ! "iterate"  
}

a = 0
for (a <- 1 to 3) {
  helloActor ! "add"  
}
a = 0
for (a <- 1 to 10) {
  helloActor ! "iterate"  
}

Scalafiddle link.

我的问题:

  1. 这是如何在akka actor中创建动态变化的工作缓冲区的?也许有更好的方法来解决这个问题?
  2. 如果这不是最糟糕的想法那么实施呢。有一个iteratorCounter对我来说有点奇怪。要求迭代器长度移动其指针。还有其他方法吗?
  3. 也许Manager在工人拉动关系中根本不应该有一个更改缓冲区,工人能够生成新任务?目前我无法看到这个解决方案有任何问题,但我可能没有问正确的问题。

1 个答案:

答案 0 :(得分:2)

  1. 我建议使用mutable.ArrayBuffer排队工作,而Iterator将工作推入/拉出队列,而不是immutable.Queueenqueue/dequeue。从您的示例代码中可以看出,您正在以FIFO方式工作,因此队列最适合您的需求。

  2. 我不建议使用Iterator(可变)作为指针。特别是,除了next/hasNext之外,在调用方法之后使用迭代器是不安全的(参见Scala doc)。使用FIFO队列时,不需要迭代器。

  3. 让经理人保持内部队列/地图/等以跟踪工作人员的任务是很常见的。只要适用,我会在可变集合中选择一个不可变的集合。如果有必要,请将不可变集合设为private var,但如果您想避免任何hot-swap,则可以通过context.become var演员的内部状态。

    < / LI>

    Re:使用Akka actor的拉模型的工人系统,这里是nice article。 Lightbend还提供了一个基于拉模型的distributed worker system示例应用程序。