控制Akka actor中的消息流

时间:2014-04-20 01:30:47

标签: scala akka message-queue actor future

我有一个演员使用Akka执行一个需要一些时间才能完成的动作,因为它必须从网络下载文件。

  def receive = {
    case songId: String => {
      Future {
        val futureFile = downloadFile(songId)

        for (file <- futureFile) {
          val fileName = doSomenthingWith(file)
          otherActor ! fileName
        }
      }
    }
  }

我想控制这个演员的消息流。如果我尝试同时下载太多文件,我就会遇到网络瓶颈。问题是我在actor接收中使用Future,因此,方法退出并且actor已准备好处理新消息。如果我删除Future,我每次只会下载一个文件。

限制每单位时间处理的邮件数量的最佳方法是什么?有没有更好的方法来设计此代码?

3 个答案:

答案 0 :(得分:3)

Akka有一个contrib项目,它提供了一个限制实现(http://letitcrash.com/post/28901663062/throttling-messages-in-akka-2)。如果你坐在实际的下载演员面前,那么你可以有效地限制进入该演员的消息的速度。如果下载时间比预期的要长,你可能仍然需要更多的下载,那么它并非100%完美,但它可能是一个非常简单的实现,我们使用它相当多效果很好。

另一个选项可能是use a pool下载actor并删除未来并允许actor执行此阻止,以便它们一次只能处理一条消息。因为你要让他们阻止,我会建议giving them their own Dispatcher (ExecutionContext)这样阻止不会对主Akka Dispatcher产生负面影响。如果您这样做,那么池大小本身就代表了允许的最大同时下载次数。

这两种解决方案几乎都是开箱即用的解决方案。解决方案不需要太多自定义逻辑来支持您的用例。

修改

我也认为提及Work Pulling Pattern会很好。使用这种方法,您仍然可以使用池,然后使用前面的单个工作分配器。每个工作人员(下载演员)都可以执行下载(仍然使用Future)并且仅在Future完全完成意味着下载完成时才从工作分发者请求新工作(拉)。

答案 1 :(得分:0)

如果你有想要发生的同步下载量的上限,你可以'回答'演员说下载完成并释放一个点来下载另一个文件:

case object AckFileRequest

class ActorExample(otherActor:ActorRef, maxFileRequests:Int = 1) extends Actor {

  var fileRequests = 0

  def receive = {
    case songId: String if fileRequests < maxFileRequests =>
      fileRequests += 1
      val thisActor = self
      Future {
        val futureFile = downloadFile(songId)
        //not sure if you're returning the downloaded file or a future here, 
        //but you can move this to wherever the downloaded file is and ack
        thisActor ! AckFileRequest

        for (file <- futureFile) {
          val fileName = doSomenthingWith(file)
          otherActor ! fileName
        }
      }
    case songId: String =>
      //Do some throttling here
      val thisActor = self
      context.system.scheduler.scheduleOnce(1 second, thisActor, songId)
    case AckFileRequest => fileRequests -= 1
  }
}

在此示例中,如果文件请求过多,则我们将此songId请求置于保持状态,并在1秒后将其重新排队等待处理。你可以明显地改变它,但是你认为可以直接将消息直接发送回演员或者进行其他限制,取决于你的用例。

答案 2 :(得分:0)

有一个消息Throttling的contrib实现,described here

代码非常简单:

// A simple actor that prints whatever it receives
class Printer extends Actor {
  def receive = {
    case x => println(x)
  }
}

val printer = system.actorOf(Props[Printer], "printer")

// The throttler for this example, setting the rate
val throttler = system.actorOf(Props(classOf[TimerBasedThrottler], 3 msgsPer 1.second))

// Set the target
throttler ! SetTarget(Some(printer))
// These three messages will be sent to the printer immediately
throttler ! "1"
throttler ! "2"
throttler ! "3"
// These two will wait at least until 1 second has passed
throttler ! "4"
throttler ! "5"