我有一个带负载均衡的工作池,其定义如下:
class Worker(workerNr: Int) extends Actor with Stash
...
val workers = (1 to poolSize).map(c => context.actorOf(Props(() => new Worker(c)).withDispatcher("stash-dispatcher"), "worker" + c))
val pool = context.actorOf(Props[Worker].withRouter(SmallestMailboxRouter(routees = workers)))
...
pool ! Request("do something")
现在这个工作者演员不是无国籍的,并且在他将请求转发给另一个演员(执行实际工作)并将所有后续请求存储起来之后使用,直到他获得当前请求的响应(可以采取而)。然后他将响应发送给请求的actor,解除所有被删除的消息,并在切换回unbecome后处理下一个请求。
case request@Request(_) => {
val requestor = sender
requestHandler ! request
become {
case response@Response(_) => {
requestor ! response
unstashAll
unbecome
}
case msg => stash
}
}
我的问题是SmallestMailboxRouter,我正在使用。它将消息路由到具有最小邮箱的worker。但是由于工作人员没有阻止,并且藏匿了他们目前无法处理的消息,他们的邮箱总是很空(与他们的存储相反)。
我想要一个路由器,它将消息路由到具有最小存储的工作者。我想过自己实现一个路由器这样做,但是看implementation of Stash,似乎我甚至无法访问存储大小,因为存储本身对于存储特征是私有的。
private var theStash = Vector.empty[Envelope]
有没有办法做到这一点,或者这是实现具有负载平衡的工作池的错误方法?
答案 0 :(得分:4)
回答你最后问的这个问题:“有没有办法做到这一点,或者这是用错误的方法实现负载均衡的工作池?”。
以下是我使用负载平衡实现工作池的方法:
有一个WorkerManager
actor接收作业请求。它立即将它们放入自己的队列中。这可以是保存作业请求Job
的任何类型的队列,例如Queue[Job]
。 WorkerManager
还有一个工作人员列表,其中包含了List[(ActorRef, Option[Job]]
。
每当WorkerManager
收到Job
请求并且在将其放入队列之后,它就可以检查分配的作业列表中是否有空闲的actor,即(ActorRef, None)
。如果是,则它在该列表中为该工作者actor设置分配的作业,并向该actor发送Job
消息。如果没有空闲工作人员WorkerManager
根本不做任何事情,并等待其中一个工作人员回复作业完成消息。
另一方面,只要Worker
完成对Job
的处理,它就会使用WorkerManager
ID回复Job
,而WorkerManager
会从列表中删除该作业已分配/正在运行的作业如果Worker
失败,可以根据需要使用相同的作业重新启动。
您可以选择回复Client
的人 - 可以是Worker
或WorkerManager
。出于这些目的,您可能希望将客户端ActorRef
与Job
消息一起发送到Worker
。
并发队列修改或与维护队列相关的任何竞争条件都没有问题,因为Actors逐个处理消息,因此WorkerManager
将始终按顺序处理队列。
此外Worker
可以是具有状态转换超时的状态机,以避免永远等待它。
工作人员可以由WorkerManager
创建,也可以单独创建,并通过发送注册消息向WorkerManager
注册。可能有多个WorkerManager
演员使用其中一种路由算法(循环法等)获取任务。
修改
显然有一种模式:) - 它被称为工作拉动模式或其他东西。