2010-05-03 19:32:40 +02:00
|
|
|
/**
|
2011-07-14 16:03:08 +02:00
|
|
|
* Copyright (C) 2009-2011 Typesafe Inc. <http://www.typesafe.com>
|
2010-05-03 19:32:40 +02:00
|
|
|
*/
|
|
|
|
|
|
2010-10-26 12:49:25 +02:00
|
|
|
package akka.routing
|
2010-02-13 21:45:35 +01:00
|
|
|
|
2011-07-28 15:48:03 +03:00
|
|
|
import akka.actor._
|
2011-08-27 08:10:25 +03:00
|
|
|
|
2011-11-25 14:49:09 +01:00
|
|
|
import akka.japi.Creator
|
2011-12-08 14:30:57 +01:00
|
|
|
import java.lang.reflect.InvocationTargetException
|
|
|
|
|
import akka.config.ConfigurationException
|
|
|
|
|
import akka.routing.Routing.Broadcast
|
|
|
|
|
import akka.actor.DeploymentConfig.Deploy
|
|
|
|
|
import java.util.concurrent.atomic.AtomicInteger
|
2011-12-11 22:34:38 +01:00
|
|
|
import akka.dispatch.Future
|
|
|
|
|
import akka.util.{ Duration, ReflectiveAccess }
|
|
|
|
|
import java.util.concurrent.TimeUnit
|
|
|
|
|
import akka.AkkaException
|
2011-08-27 08:10:25 +03:00
|
|
|
|
2011-11-11 20:05:53 +01:00
|
|
|
sealed trait RouterType
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Used for declarative configuration of Routing.
|
|
|
|
|
*
|
|
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
|
|
|
|
*/
|
|
|
|
|
object RouterType {
|
|
|
|
|
|
2011-12-08 14:30:57 +01:00
|
|
|
/**
|
|
|
|
|
* A RouterType that indicates no routing - i.e. direct message.
|
|
|
|
|
*/
|
2011-12-11 22:34:38 +01:00
|
|
|
object NoRouter extends RouterType
|
2011-11-11 20:05:53 +01:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A RouterType that randomly selects a connection to send a message to.
|
|
|
|
|
*/
|
|
|
|
|
object Random extends RouterType
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A RouterType that selects the connection by using round robin.
|
|
|
|
|
*/
|
|
|
|
|
object RoundRobin extends RouterType
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A RouterType that selects the connection by using scatter gather.
|
|
|
|
|
*/
|
|
|
|
|
object ScatterGather extends RouterType
|
|
|
|
|
|
2011-11-24 16:35:37 +01:00
|
|
|
/**
|
|
|
|
|
* A RouterType that broadcasts the messages to all connections.
|
|
|
|
|
*/
|
|
|
|
|
object Broadcast extends RouterType
|
|
|
|
|
|
2011-11-11 20:05:53 +01:00
|
|
|
/**
|
|
|
|
|
* A RouterType that selects the connection based on the least amount of cpu usage
|
|
|
|
|
*/
|
|
|
|
|
object LeastCPU extends RouterType
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A RouterType that select the connection based on the least amount of ram used.
|
|
|
|
|
*/
|
|
|
|
|
object LeastRAM extends RouterType
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A RouterType that select the connection where the actor has the least amount of messages in its mailbox.
|
|
|
|
|
*/
|
|
|
|
|
object LeastMessages extends RouterType
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A user-defined custom RouterType.
|
|
|
|
|
*/
|
|
|
|
|
case class Custom(implClass: String) extends RouterType
|
2011-12-08 14:30:57 +01:00
|
|
|
|
2011-11-11 20:05:53 +01:00
|
|
|
}
|
|
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
/**
|
|
|
|
|
* An {@link AkkaException} thrown when something goes wrong while routing a message
|
|
|
|
|
*/
|
|
|
|
|
class RoutingException(message: String) extends AkkaException(message)
|
|
|
|
|
|
2011-11-11 20:05:53 +01:00
|
|
|
/**
|
|
|
|
|
* Contains the configuration to create local and clustered routed actor references.
|
|
|
|
|
* Routed ActorRef configuration object, this is thread safe and fully sharable.
|
|
|
|
|
*/
|
|
|
|
|
case class RoutedProps private[akka] (
|
2011-11-24 16:35:37 +01:00
|
|
|
routerFactory: () ⇒ Router,
|
2011-12-08 14:30:57 +01:00
|
|
|
connectionManager: ConnectionManager) {
|
2011-11-25 14:49:09 +01:00
|
|
|
|
|
|
|
|
// Java API
|
2011-12-08 14:30:57 +01:00
|
|
|
def this(creator: Creator[Router], connectionManager: ConnectionManager) {
|
|
|
|
|
this(() ⇒ creator.create(), connectionManager)
|
2011-11-25 14:49:09 +01:00
|
|
|
}
|
2011-12-08 14:30:57 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2011-12-11 22:34:38 +01:00
|
|
|
* A RoutedActorRef is an ActorRef that has a set of connected ActorRef and it uses a Router to
|
|
|
|
|
* send a message to on (or more) of these actors.
|
2011-12-08 14:30:57 +01:00
|
|
|
*/
|
|
|
|
|
private[akka] class RoutedActorRef(_system: ActorSystemImpl, _props: Props, _supervisor: InternalActorRef, _path: ActorPath)
|
|
|
|
|
extends LocalActorRef(
|
|
|
|
|
_system,
|
|
|
|
|
_props.copy(creator = _props.routerConfig),
|
|
|
|
|
_supervisor,
|
|
|
|
|
_path) {
|
|
|
|
|
|
|
|
|
|
val route: Routing.Route = _props.routerConfig.createRoute(_props.creator, actorContext)
|
|
|
|
|
|
|
|
|
|
override def !(message: Any)(implicit sender: ActorRef = null) {
|
|
|
|
|
route(message) match {
|
2011-12-11 22:34:38 +01:00
|
|
|
case null ⇒ super.!(message)(sender)
|
|
|
|
|
case ref: ActorRef ⇒ ref.!(message)(sender)
|
|
|
|
|
case refs: Traversable[ActorRef] ⇒
|
|
|
|
|
message match {
|
|
|
|
|
case Broadcast(m) ⇒ refs foreach (_.!(m)(sender))
|
|
|
|
|
case _ ⇒ refs foreach (_.!(message)(sender))
|
|
|
|
|
}
|
2011-12-08 14:30:57 +01:00
|
|
|
}
|
|
|
|
|
}
|
2011-12-11 22:34:38 +01:00
|
|
|
|
|
|
|
|
// TODO (HE) : Should the RoutedActorRef also override "?"?
|
|
|
|
|
// If not how then Broadcast messages cannot be sent via ? -
|
|
|
|
|
// which it is in some test cases at the moment.
|
2011-11-11 20:05:53 +01:00
|
|
|
}
|
|
|
|
|
|
2011-12-08 14:30:57 +01:00
|
|
|
trait RouterConfig extends Function0[Actor] {
|
|
|
|
|
def adaptFromDeploy(deploy: Option[Deploy]): RouterConfig
|
2011-08-12 10:03:33 +03:00
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
def createRoute(creator: () ⇒ Actor, actorContext: ActorContext): Routing.Route
|
2011-08-12 10:03:33 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
2011-12-11 22:34:38 +01:00
|
|
|
* A Router is responsible for sending a message to one (or more) of its connections.
|
2011-12-08 14:30:57 +01:00
|
|
|
*
|
|
|
|
|
* @author <a href="http://jonasboner.com">Jonas Bonér</a>
|
2011-08-27 08:10:25 +03:00
|
|
|
*/
|
2011-12-08 14:30:57 +01:00
|
|
|
trait Router {
|
|
|
|
|
}
|
2011-08-27 08:10:25 +03:00
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* A Helper class to create actor references that use routing.
|
|
|
|
|
*/
|
2011-07-28 15:48:03 +03:00
|
|
|
object Routing {
|
2011-10-07 15:22:36 +02:00
|
|
|
|
2011-07-28 15:48:03 +03:00
|
|
|
sealed trait RoutingMessage
|
2011-05-24 19:04:25 +02:00
|
|
|
|
2011-08-27 08:10:25 +03:00
|
|
|
/**
|
2011-10-07 19:42:10 +02:00
|
|
|
* Used to broadcast a message to all connections in a router. E.g. every connection gets the message
|
|
|
|
|
* regardless of their routing algorithm.
|
2011-08-27 08:10:25 +03:00
|
|
|
*/
|
2011-07-28 15:48:03 +03:00
|
|
|
case class Broadcast(message: Any) extends RoutingMessage
|
2011-08-17 10:21:27 +03:00
|
|
|
|
2011-10-11 11:18:47 +02:00
|
|
|
def createCustomRouter(implClass: String): Router = {
|
2011-12-08 14:30:57 +01:00
|
|
|
ReflectiveAccess.createInstance(implClass, Array[Class[_]](), Array[AnyRef]()) match {
|
|
|
|
|
case Right(router) ⇒ router.asInstanceOf[Router]
|
|
|
|
|
case Left(exception) ⇒
|
|
|
|
|
val cause = exception match {
|
|
|
|
|
case i: InvocationTargetException ⇒ i.getTargetException
|
|
|
|
|
case _ ⇒ exception
|
|
|
|
|
}
|
2011-05-24 19:04:25 +02:00
|
|
|
|
2011-12-08 14:30:57 +01:00
|
|
|
throw new ConfigurationException("Could not instantiate custom Router of [" +
|
|
|
|
|
implClass + "] due to: " + cause, cause)
|
2011-07-28 15:48:03 +03:00
|
|
|
}
|
|
|
|
|
}
|
2011-09-08 11:02:17 +02:00
|
|
|
|
2011-12-08 14:30:57 +01:00
|
|
|
type Route = (Any) ⇒ AnyRef
|
2011-07-28 15:48:03 +03:00
|
|
|
}
|
2010-03-04 19:02:23 +01:00
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
/**
|
|
|
|
|
* Routing configuration that indicates no routing.
|
|
|
|
|
* Oxymoron style.
|
|
|
|
|
*/
|
|
|
|
|
case object NoRouter extends RouterConfig {
|
|
|
|
|
def adaptFromDeploy(deploy: Option[Deploy]) = null
|
|
|
|
|
|
|
|
|
|
def createRoute(creator: () ⇒ Actor, actorContext: ActorContext) = null
|
|
|
|
|
|
|
|
|
|
def apply(): Actor = null
|
|
|
|
|
}
|
|
|
|
|
|
2011-07-28 15:48:03 +03:00
|
|
|
/**
|
2011-12-08 14:30:57 +01:00
|
|
|
* A Router that uses round-robin to select a connection. For concurrent calls, round robin is just a best effort.
|
|
|
|
|
* <br>
|
|
|
|
|
* Please note that providing both 'nrOfInstances' and 'targets' does not make logical sense as this means
|
|
|
|
|
* that the round robin should both create new actors and use the 'targets' actor(s).
|
|
|
|
|
* In this case the 'nrOfInstances' will be ignored and the 'targets' will be used.
|
|
|
|
|
* <br>
|
|
|
|
|
* <b>The</b> configuration parameter trumps the constructor arguments. This means that
|
|
|
|
|
* if you provide either 'nrOfInstances' or 'targets' to during instantiation they will
|
|
|
|
|
* be ignored if the 'nrOfInstances' is defined in the configuration file for the actor being used.
|
2011-07-28 15:48:03 +03:00
|
|
|
*/
|
2011-12-08 14:30:57 +01:00
|
|
|
case class RoundRobinRouter(nrOfInstances: Int = 0, targets: Iterable[ActorRef] = Nil)
|
|
|
|
|
extends Router with RouterConfig {
|
2011-07-28 15:48:03 +03:00
|
|
|
|
2011-12-08 14:30:57 +01:00
|
|
|
def adaptFromDeploy(deploy: Option[Deploy]): RouterConfig = {
|
|
|
|
|
deploy match {
|
2011-12-11 22:34:38 +01:00
|
|
|
case Some(d) ⇒
|
|
|
|
|
// In case there is a config then use this over any programmed settings.
|
|
|
|
|
copy(nrOfInstances = d.nrOfInstances.factor, targets = Nil)
|
|
|
|
|
case _ ⇒ this
|
2011-11-24 16:35:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-08 14:30:57 +01:00
|
|
|
def apply(): Actor = new Actor {
|
|
|
|
|
def receive = {
|
|
|
|
|
case _ ⇒
|
2011-11-24 16:35:37 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-08 14:30:57 +01:00
|
|
|
def createRoute(creator: () ⇒ Actor, context: ActorContext): Routing.Route = {
|
|
|
|
|
val routees: Vector[ActorRef] = (nrOfInstances, targets) match {
|
|
|
|
|
case (0, Nil) ⇒ throw new IllegalArgumentException("Insufficient information - missing configuration.")
|
2011-12-11 22:34:38 +01:00
|
|
|
case (x, Nil) ⇒ (1 to x).map(_ ⇒ context.actorOf(context.props.copy(creator = creator, routerConfig = NoRouter)))(scala.collection.breakOut)
|
|
|
|
|
case (x, xs) ⇒ Vector.empty[ActorRef] ++ xs
|
2011-08-12 10:03:33 +03:00
|
|
|
}
|
2011-07-28 15:48:03 +03:00
|
|
|
|
2011-12-08 14:30:57 +01:00
|
|
|
val next = new AtomicInteger(0)
|
2011-08-12 10:03:33 +03:00
|
|
|
|
2011-12-08 14:30:57 +01:00
|
|
|
def getNext(): ActorRef = {
|
|
|
|
|
routees(next.getAndIncrement % routees.size)
|
|
|
|
|
}
|
2011-08-18 11:35:14 +02:00
|
|
|
|
2011-12-08 14:30:57 +01:00
|
|
|
{
|
2011-12-11 22:34:38 +01:00
|
|
|
case msg: AutoReceivedMessage ⇒ null // TODO (HE): how should system messages be handled?
|
|
|
|
|
case Broadcast(msg) ⇒ routees
|
|
|
|
|
case msg ⇒ getNext()
|
2011-07-28 15:48:03 +03:00
|
|
|
}
|
2011-08-12 10:03:33 +03:00
|
|
|
}
|
2011-12-11 22:34:38 +01:00
|
|
|
}
|
2011-08-12 10:03:33 +03:00
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
/**
|
|
|
|
|
* A Router that randomly selects one of the target connections to send a message to.
|
|
|
|
|
* <br>
|
|
|
|
|
* Please note that providing both 'nrOfInstances' and 'targets' does not make logical sense as this means
|
|
|
|
|
* that the random router should both create new actors and use the 'targets' actor(s).
|
|
|
|
|
* In this case the 'nrOfInstances' will be ignored and the 'targets' will be used.
|
|
|
|
|
* <br>
|
|
|
|
|
* <b>The</b> configuration parameter trumps the constructor arguments. This means that
|
|
|
|
|
* if you provide either 'nrOfInstances' or 'targets' to during instantiation they will
|
|
|
|
|
* be ignored if the 'nrOfInstances' is defined in the configuration file for the actor being used.
|
|
|
|
|
*/
|
|
|
|
|
case class RandomRouter(nrOfInstances: Int = 0, targets: Iterable[ActorRef] = Nil)
|
|
|
|
|
extends Router with RouterConfig {
|
2011-05-17 21:15:27 +02:00
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
def adaptFromDeploy(deploy: Option[Deploy]): RouterConfig = {
|
|
|
|
|
deploy match {
|
|
|
|
|
case Some(d) ⇒
|
|
|
|
|
// In case there is a config then use this over any programmed settings.
|
|
|
|
|
copy(nrOfInstances = d.nrOfInstances.factor, targets = Nil)
|
|
|
|
|
case _ ⇒ this
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-08-18 11:35:14 +02:00
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
def apply(): Actor = new Actor {
|
|
|
|
|
def receive = {
|
|
|
|
|
case _ ⇒
|
2011-05-21 15:37:09 +02:00
|
|
|
}
|
2011-08-12 10:03:33 +03:00
|
|
|
}
|
|
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
import java.security.SecureRandom
|
2011-08-12 10:03:33 +03:00
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
private val random = new ThreadLocal[SecureRandom] {
|
|
|
|
|
override def initialValue = SecureRandom.getInstance("SHA1PRNG")
|
|
|
|
|
}
|
2011-08-12 10:03:33 +03:00
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
def createRoute(creator: () ⇒ Actor, context: ActorContext): Routing.Route = {
|
|
|
|
|
val routees: Vector[ActorRef] = (nrOfInstances, targets) match {
|
|
|
|
|
case (0, Nil) ⇒ throw new IllegalArgumentException("Insufficient information - missing configuration.")
|
|
|
|
|
case (x, Nil) ⇒ (1 to x).map(_ ⇒ context.actorOf(context.props.copy(creator = creator, routerConfig = NoRouter)))(scala.collection.breakOut)
|
|
|
|
|
case (x, xs) ⇒ Vector.empty[ActorRef] ++ xs
|
|
|
|
|
}
|
2011-05-21 15:37:09 +02:00
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
def getNext(): ActorRef = {
|
|
|
|
|
routees(random.get.nextInt(routees.size))
|
|
|
|
|
}
|
2011-08-12 10:03:33 +03:00
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
{
|
|
|
|
|
case msg: AutoReceivedMessage ⇒ null // TODO (HE): how should system messages be handled?
|
|
|
|
|
case Broadcast(msg) ⇒ routees
|
|
|
|
|
case msg ⇒ getNext()
|
2011-08-12 10:03:33 +03:00
|
|
|
}
|
2011-05-17 21:15:27 +02:00
|
|
|
}
|
|
|
|
|
}
|
2011-08-17 10:21:27 +03:00
|
|
|
|
2011-08-29 15:50:40 +02:00
|
|
|
/**
|
2011-12-11 22:34:38 +01:00
|
|
|
* A Router that uses broadcasts a message to all its connections.
|
|
|
|
|
* <br>
|
|
|
|
|
* Please note that providing both 'nrOfInstances' and 'targets' does not make logical sense as this means
|
|
|
|
|
* that the random router should both create new actors and use the 'targets' actor(s).
|
|
|
|
|
* In this case the 'nrOfInstances' will be ignored and the 'targets' will be used.
|
|
|
|
|
* <br>
|
|
|
|
|
* <b>The</b> configuration parameter trumps the constructor arguments. This means that
|
|
|
|
|
* if you provide either 'nrOfInstances' or 'targets' to during instantiation they will
|
|
|
|
|
* be ignored if the 'nrOfInstances' is defined in the configuration file for the actor being used.
|
2011-08-17 10:21:27 +03:00
|
|
|
*/
|
2011-12-11 22:34:38 +01:00
|
|
|
case class BroadcastRouter(nrOfInstances: Int = 0, targets: Iterable[ActorRef] = Nil)
|
|
|
|
|
extends Router with RouterConfig {
|
2011-08-17 10:21:27 +03:00
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
def adaptFromDeploy(deploy: Option[Deploy]): RouterConfig = {
|
|
|
|
|
deploy match {
|
|
|
|
|
case Some(d) ⇒
|
|
|
|
|
// In case there is a config then use this over any programmed settings.
|
|
|
|
|
copy(nrOfInstances = d.nrOfInstances.factor, targets = Nil)
|
|
|
|
|
case _ ⇒ this
|
2011-08-17 10:21:27 +03:00
|
|
|
}
|
2011-12-11 22:34:38 +01:00
|
|
|
}
|
2011-08-17 10:21:27 +03:00
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
def apply(): Actor = new Actor {
|
|
|
|
|
def receive = {
|
|
|
|
|
case _ ⇒
|
|
|
|
|
}
|
2011-08-17 10:21:27 +03:00
|
|
|
}
|
|
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
def createRoute(creator: () ⇒ Actor, context: ActorContext): Routing.Route = {
|
|
|
|
|
val routees: Vector[ActorRef] = (nrOfInstances, targets) match {
|
|
|
|
|
case (0, Nil) ⇒ throw new IllegalArgumentException("Insufficient information - missing configuration.")
|
|
|
|
|
case (x, Nil) ⇒ (1 to x).map(_ ⇒ context.actorOf(context.props.copy(creator = creator, routerConfig = NoRouter)))(scala.collection.breakOut)
|
|
|
|
|
case (x, xs) ⇒ Vector.empty[ActorRef] ++ xs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
case msg: AutoReceivedMessage ⇒ null // TODO (HE): how should system messages be handled?
|
|
|
|
|
case Broadcast(msg) ⇒ routees
|
|
|
|
|
case msg ⇒ routees
|
|
|
|
|
}
|
2011-08-17 10:21:27 +03:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-12-11 22:34:38 +01:00
|
|
|
// TODO (HE) : Correct description below
|
2011-08-29 15:50:40 +02:00
|
|
|
/**
|
2011-12-11 22:34:38 +01:00
|
|
|
* Simple router that broadcasts the message to all connections, and replies with the first response.
|
|
|
|
|
* Scatter-gather pattern will be applied only to the messages broadcast using Future
|
|
|
|
|
* (wrapped into {@link Routing.Broadcast} and sent with "?" method).
|
|
|
|
|
* For the messages sent in a fire-forget mode, the router would behave as {@link RoundRobinRouter}
|
2011-08-17 10:21:27 +03:00
|
|
|
*/
|
2011-12-11 22:34:38 +01:00
|
|
|
case class ScatterGatherFirstCompletedRouter(nrOfInstances: Int = 0, targets: Iterable[ActorRef] = Nil) extends Router with RouterConfig {
|
|
|
|
|
|
|
|
|
|
def adaptFromDeploy(deploy: Option[Deploy]): RouterConfig = {
|
|
|
|
|
deploy match {
|
|
|
|
|
case Some(d) ⇒
|
|
|
|
|
// In case there is a config then use this over any programmed settings.
|
|
|
|
|
copy(nrOfInstances = d.nrOfInstances.factor, targets = Nil)
|
|
|
|
|
case _ ⇒ this
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def apply(): Actor = new Actor {
|
|
|
|
|
def receive = {
|
|
|
|
|
case _ ⇒
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def createRoute(creator: () ⇒ Actor, context: ActorContext): Routing.Route = {
|
|
|
|
|
val routees: Vector[ActorRef] = (nrOfInstances, targets) match {
|
|
|
|
|
case (0, Nil) ⇒ throw new IllegalArgumentException("Insufficient information - missing configuration.")
|
|
|
|
|
case (x, Nil) ⇒ (1 to x).map(_ ⇒ context.actorOf(context.props.copy(creator = creator, routerConfig = NoRouter)))(scala.collection.breakOut)
|
|
|
|
|
case (x, xs) ⇒ Vector.empty[ActorRef] ++ xs
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
def scatterGather[S, G >: S](message: Any, t: Timeout): Future[G] = {
|
|
|
|
|
val responses = routees.flatMap { actor ⇒
|
|
|
|
|
try {
|
|
|
|
|
if (actor.isTerminated) None else Some(actor.?(message, t).asInstanceOf[Future[S]])
|
|
|
|
|
} catch {
|
|
|
|
|
case e: Exception ⇒ None
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!responses.isEmpty) throw new RoutingException("No connections can process the message [%s] sent to scatter-gather router" format (message))
|
|
|
|
|
else {
|
|
|
|
|
implicit val messageDispatcher = context.dispatcher
|
|
|
|
|
implicit val timeout = t
|
|
|
|
|
Future.firstCompletedOf(responses)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// TODO (HE) : Timeout and Future should be updated to new strategy - or hardcoded value below should at least be removed!
|
|
|
|
|
{
|
|
|
|
|
case msg: AutoReceivedMessage ⇒ null // TODO (HE): how should system messages be handled?
|
|
|
|
|
case Broadcast(msg) ⇒ routees
|
|
|
|
|
case msg ⇒ scatterGather(msg, Timeout(Duration(5000, TimeUnit.MILLISECONDS)))
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-08-17 10:21:27 +03:00
|
|
|
}
|