Hi Jean,

> 20 jan 2015 kl. 15:26 skrev Jean Helou <[email protected]>:
> 
> Hello Roland, 
>  
> thanks for taking the time to answer. 
> 
> your description very much sounds like an Actor should be handling the pool, 
> not a router.
> 
> Alright, however I am still curious as to why the BalancingPool doesn't 
> handle resize and why RoundRobinPool + BalancingDispatcher is deprecated. 

The BalancingDispatcher is a very particular setup: a thread pool of size N 
with N actors that all pull from the same queue. Thread pool sizes are not 
changeable at runtime in Akka.

>  
> You can use the DefaultResizer stand-alone (for determining what to do) and 
> you can also use the RoundRobinRoutingLogic stand-alone (for determining 
> where to send to); then the only missing piece is to place these inside an 
> Actor that will then start new Actors (easy) or stop Actors in a coordinated 
> fashion (straight-forward: just send a termination request and take it out of 
> the list of routees you pass into the RoutingLogic).
> 
>  starting & stopping actors is indeed easy, using the RoundRobinRoutingLogic 
> is fairly straightforward. Implementing the management messages is starting 
> to be a bit of a bother. Using DefaultResizer for anything else than a fixed 
> size actor based pool is really not trivial.
> 
> As I said in my introduction 
>  I experimented with a fixed size pool but ultimately I would prefer an 
> elastic pool able to spawn despawn actors depending on request pressure
> 
> Hopefully the following will fill the fixed size pool with a round robin 
> behavior , an enforced fixed size  and can be safely combined with 
> routeeProps having a BalancingDispatcher.

As I said BalancingDispatcher has a statically configured number of threads. 
Best performance is achieved by matching this number to the number of actors, 
which avoids (most of) the thread hopping that would otherwise occur.

> 
> package actors.browser
>  
> import akka.actor.{Terminated, Props, ActorLogging, Actor}
> import akka.routing._
>  
> import scala.collection.immutable.IndexedSeq
>  
> class RoundRobinFixedSizeActorPool(routeeProps:Props, nbActors:Int) extends 
> Actor with ActorLogging{
>   val resizer = DefaultResizer(nbActors, nbActors)
>   val routingLogic = RoundRobinRoutingLogic()
>  
>   var routees: IndexedSeq[Routee] = (1 until resizer.lowerBound).map{_=>
>     val routee = context.actorOf(routeeProps)
>     context.watch(routee)
>     ActorRefRoutee(routee)
>   }
>  
>   override def receive: Receive = {
>     case Terminated(actor) if routees.contains(ActorRefRoutee(actor))=>
>       routees=routees.filterNot(_ == ActorRefRoutee(actor))
>       val routee = context.actorOf(routeeProps)
>       context.watch(routee)
>       routees=routees :+ ActorRefRoutee(routee)
>     case GetRoutees =>
>       sender() ! Routees(routees)
>     case evt => routingLogic.select(evt, routees).send(evt,sender())
>   }
> }
> object RoundRobinFixedSizeActorPool{
>   def props(props:Props, nbActors:Int)=Props(new 
> RoundRobinFixedSizeActorPool(props, nbActors))
> }
> 
> How would you recommend going from there to a pressure sensitive actor pool 
> oscillating between a min and a max ? 
> 
> Does the following look correct (I am unsure how to write tests for this ... 
> ) ?
> 
> package actors.browser
>  
> import java.util.concurrent.atomic.AtomicLong
>  
> import akka.actor._
> import akka.routing._
>  
> import scala.collection.immutable.IndexedSeq
>  
> class RoundRobinActorPool(routeeProps: Props, minRoutees: Int, maxRoutees: 
> Int) extends Actor with ActorLogging {
>   val resizer: Resizer = DefaultResizer(minRoutees, maxRoutees)
>   val nbMsgs = new AtomicLong()

This should be a simple Long, no need to be atomic within an Actor.

>   val routingLogic = RoundRobinRoutingLogic()
>  
>   var routees: IndexedSeq[Routee] = IndexedSeq()
>   upsize(minRoutees)
>  
>   override def receive: Receive = {
>     case Terminated(actor) if routees.contains(ActorRefRoutee(actor)) =>
>       routees = routees.filterNot(_ == ActorRefRoutee(actor))

The unhandled Terminated messages will then go to the routees which is probably 
not intended.

>     case GetRoutees =>
>       sender() ! Routees(routees)
>     case evt =>
>       if (resizer.isTimeForResize(nbMsgs.incrementAndGet())) {
>         resizer.resize(routees)
>       }

This should move into the resize() method below (which should also be called).

>  
>       routingLogic.select(evt, routees).send(evt, sender())
>   }
>  
>   private def resize(delta: Int) = {
>     if (delta < 0) downsize(delta)
>     else upsize(delta)
>   }
>  
>   private def downsize(delta: Int) = {
>     val (killed, remaining) = this.routees.splitAt(delta)
>     this.routees = remaining
>     killed.foreach(_.send(PoisonPill, self))
>   }
>  
>   private def upsize(delta: Int) = {
>     routees = routees ++ (1 until minRoutees).map { _ =>
>       val routee = context.actorOf(routeeProps)
>       context.watch(routee)
>       ActorRefRoutee(routee)
>     }
>   }
> }
>  
> object RoundRobinActorPool {
>   def props(props: Props,minRoutees: Int, maxRoutees: Int) = Props(new 
> RoundRobinActorPool(props, minRoutees, maxRoutees))
> }
> 
> Are you aware of a contrib/3rd party package which would offer Actor based 
> pools while preserving most of the semantics of router based ones ?

The semantics of routers are equivalent to Actors, that is a very important 
point. Routers are just an optimization that eliminates one queueing step, but 
that is rarely necessary since an actor can process several million messages 
per second.

Actor based pools are a lot more flexible in what they can do, and they are a 
lot easier to write and reason about due to being encapsulated within an Actor. 
I currently am not aware of a collection of common ones, your code in this 
email is a good starting point for the use-case you outlined.

Regards,

Roland

> 
> Thanks for your help
> Jean
> 
> -- 
> >>>>>>>>>> Read the docs: http://akka.io/docs/ <http://akka.io/docs/>
> >>>>>>>>>> Check the FAQ: 
> >>>>>>>>>> http://doc.akka.io/docs/akka/current/additional/faq.html 
> >>>>>>>>>> <http://doc.akka.io/docs/akka/current/additional/faq.html>
> >>>>>>>>>> Search the archives: https://groups.google.com/group/akka-user 
> >>>>>>>>>> <https://groups.google.com/group/akka-user>
> --- 
> You received this message because you are subscribed to the Google Groups 
> "Akka User List" group.
> To unsubscribe from this group and stop receiving emails from it, send an 
> email to [email protected] 
> <mailto:[email protected]>.
> To post to this group, send email to [email protected] 
> <mailto:[email protected]>.
> Visit this group at http://groups.google.com/group/akka-user 
> <http://groups.google.com/group/akka-user>.
> For more options, visit https://groups.google.com/d/optout 
> <https://groups.google.com/d/optout>.



Dr. Roland Kuhn
Akka Tech Lead
Typesafe <http://typesafe.com/> – Reactive apps on the JVM.
twitter: @rolandkuhn
 <http://twitter.com/#!/rolandkuhn>

-- 
>>>>>>>>>>      Read the docs: http://akka.io/docs/
>>>>>>>>>>      Check the FAQ: 
>>>>>>>>>> http://doc.akka.io/docs/akka/current/additional/faq.html
>>>>>>>>>>      Search the archives: https://groups.google.com/group/akka-user
--- 
You received this message because you are subscribed to the Google Groups "Akka 
User List" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at http://groups.google.com/group/akka-user.
For more options, visit https://groups.google.com/d/optout.

Reply via email to