Akka patterns
-
Upload
roman-timushev -
Category
Software
-
view
2.289 -
download
6
Transcript of Akka patterns
AkkaPatterns and anti-
patterns
Roman Timushev2014
About me
• Work at Qubell Inc.
• Scala since 2011
• Akka since 1.x
• Now: Scala, Akka, Play, MongoDB, RabbitMQ
Agenda• Anti-patterns
• Ask is so cool
• Dependency injection for the win
• I like to block it, block it
• No mutable state is so functional
• Patterns
• Actor initialization
• Safe closures
• Flow control
Ask is so cool
Tell, don’t ask:classic code
class FineService { def getFine(v: Double) = { v * greedinessService.getGreediness() }}
class GreedinessService { def getGreediness(): Double = { db.query("select ...") }}
class DbService { def query(s: String): Double = ???}
Tell, don’t ask:classic actor code
class FineService extends Actor { def receive = { case GetFine(v) => (greedinessService ? GetGreediness) .mapTo[Double].map(_ * v).pipeTo(sender()) }}
class GreedinessService extends Actor { def receive = { case GetGreediness => (db ? Query("select ...")) .mapTo[Double].pipeTo(sender()) }}
Timeout
Timeout
Tell, don’t ask
• Works perfect until loaded
• Timeouts everywhere
• equal — you don’t get the exact cause
• different — unmanageable hell
• Use thoughtful!
Tell, don’t ask:invert control flow
class FineService extends Actor { var greediness: Double = 0 def receive = { case GetFine(v) => sender() ! greediness case SetGreediness(g) => greediness = g }}
class GreedinessService extends Actor { def receive = { case ReloadGreediness => (db ? Query("select ...")) .mapTo[Double].map(SetGreediness).pipeTo(fineService) }}
Dependency injection for the win
Dependency injection:actor references
trait AkkaComponent { def actorSystem: ActorSystem}
trait DividerComponent { def divider: ActorRef}
trait CalculatorComponent { def calculator: ActorRef}
Dependency injection:actor references
trait CalculatorComponentImpl extends CalculatorComponent { this: AkkaComponent with DividerComponent =>
lazy val calculator = actorSystem.actorOf(Props(new Calculator))
class Calculator extends Actor { override val supervisorStrategy = ??? def receive = { case m: Divide => divider forward m } }
}
Dependency injection:actor props
trait AkkaComponent { def actorSystem: ActorSystem}
trait DividerComponent { def divider: Props}
trait CalculatorComponent { def calculator: ActorRef}
I like toblock it, block it
Blockingclass Blocker extends Actor {
import context._
def receive = { case GetThreadCount => sender() ! Await.result(sleep(), 10 seconds) // or sender() ! spinAwait(sleep(), 10 seconds) }
def sleep() = after(10 millis, system.scheduler)(Future.successful())
}
Blocking• Run 1000 actors
• Send 1 request to every actor
• Default executor (fork-join, parallelism-max = 64)
Spin Await
Threads 28 ~ 1000
Success rate 60% 100%
Total time 410s 0.5s
No mutable stateis so functional
Stateless actorclass StatelessActor extends Actor { def receive = { case Divide(a, b) => sender() ! (a / b) }}
class Calculator extends Actor { def receive = { case Calculate => context.actorOf(Props[StatelessActor]) ! Divide(84, 2) case result: Int => println(s"The answer is $result") }}
Just future
class Calculator extends Actor { def receive = { case Calculate => Future { 84 / 2 } pipeTo self case result: Int => println(s"The answer is $result") }}
Actor initialization
Actor initialization• Actor parameters:
• Actor constructor
• Initializing message
• Initialize yourself
• Long initialization:
• Blocking
• Switching actor behavior with dropping
• Switching actor behavior with stashing
Actor initialization:initializing message
class FineCalculator extends Actor {
var greediness = none[Double]
def receive = { case SetGreediness(g) => greediness = some(g) case GetFine(v) => sender() ! (v * greediness.get) }
}
Actor initialization:parameterized receive
class FineCalculator extends Actor {
def receive = uninitialized
def uninitialized: Receive = { case SetGreediness(m) => context.become(initialized(m)) }
def initialized(greediness: Double): Receive = { case GetFine(x) => sender() ! (x * greediness) }
}
Scala.Rxval a = Var(1)val b = Var(2)val c = Rx{ a() + b() }val cObs = Obs(c) { println(c()) }
// prints 3assert(c() == 3)
a() = 4
// prints 6assert(c() == 6)
Actor initialization:reactive receive
class FineCalculator extends Actor {
val greediness = Var(none[Double]) val actorReceive = Rx { greediness().fold(uninitialized)(initialized) } val actorReceiveObs = Obs(actorReceive) { context.become(actorReceive()) }
def receive = uninitialized def uninitialized: Receive = { case SetGreediness(g) => greediness() = some(g) } def initialized(g: Double): Receive = uninitialized orElse { case GetFine(v) => sender() ! (v * g) }
}
Actor initialization:initialize yourself
class FineCalculatorCallback extends Actor {
(context.actorSelection("..") ? GetGreediness) .mapTo[Double].map(SetGreediness).pipeTo(self)
def receive = uninitialized
def uninitialized: Receive = { case SetGreediness(m) => context.become(initialized(m)) }
def initialized(greediness: Double): Receive = { case GetFine(x) => sender() ! (x * greediness) }
}
Safe closures
Closing over actor state
When you use
• Future.apply
• future methods (map, flatMap etc.)
• scheduler
And access
• this
• vars
• context
• sender()
you are asking for trouble
Unsafe closures
class Computer extends Actor { var counter = 0
override def receive: Receive = { case Inc => val requester = sender() Future { counter += 1 // unsafe! requester ! counter } }
}
Local executortrait LocalExecutor { this: Actor =>
implicit val executor = new ExecutionContext { override def execute(runnable: Runnable): Unit = self ! runnable override def reportFailure(t: Throwable): Unit = self ! t }
def receive: Receive = { case r: Runnable => r.run() case t: Throwable => throw t }
}
Safe closures
class Computer extends Actor with LocalExecutor { var counter = 0
override def receive: Receive = super.receive orElse { case Inc => val requester = sender() Future { counter += 1 // safe requester ! counter } }
}
Flow control
Flow control:push
• The simplest option, use by default
Flow control:throttle
• You should know maximum message rate in advance
• TimerBasedThrottler (akka-contrib)
Flow control:push with ack / pull
• Acknowledge individual messages or batches
• Difference: who is first
Questions?
References