我有一个演员使用Akka执行一个需要一些时间才能完成的动作,因为它必须从网络下载文件。
def receive = {
case songId: String => {
Future {
val futureFile = downloadFile(songId)
for (file <- futureFile) {
val fileName = doSomenthingWith(file)
otherActor ! fileName
}
}
}
}
我想控制这个演员的消息流。如果我尝试同时下载太多文件,我就会遇到网络瓶颈。问题是我在actor接收中使用Future,因此,方法退出并且actor已准备好处理新消息。如果我删除Future,我每次只会下载一个文件。
限制每单位时间处理的邮件数量的最佳方法是什么?有没有更好的方法来设计此代码?
答案 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"