Fast Track to Akka
January 2, 2017 | Author: bsantoshraj | Category: N/A
Short Description
Download Fast Track to Akka...
Description
Fast Track to Akka – Part 1 Philipp Haller Typesafe Inc.
July 11-12, 2012
1
Agenda
Why Akka?
Actors
Futures
Testing Actor Systems
2
Agenda
Why Akka?
Actors
Futures
Testing Actor Systems
3
The problem
It is way too hard to build I I I
correct highly-concurrent systems truly scalable systems fault-tolerant systems
... using today’s tools
4
Vision: Simpler Concurrency and Distribution
... with a single, unified I I I
5
Programming model Runtime Open-source distribution
Manage system overload
Scale up & out
7
Replicate and distribute for Fault-Tolerance
Architecture
9
Architecture
10
Architecture
11
Where is Akka used?
FINANCE I Stock trend Analysis & Simulation I Event-driven messaging systems BETTING & GAMING I Massive multiplayer online gaming I High throughput and transactional betting
12
TELECOM I Streaming media network gateways SIMULATION I 3D simulation engines E-COMMERCE I Social media community sites
Agenda
Why Akka?
Actors
Futures
Testing Actor Systems
13
What is an Actor?
14
What is an Actor?
15
What is an Actor?
16
What is an Actor?
17
Actor Model of Concurrency
18
I
Implements Message-Passing Concurrency
I
Share NOTHING
I
Isolated lightweight processes
I
Communicates through messages
I
Asynchronous and non-blocking
I
Each actor has a mailbox (message queue)
Actor Model Benefits
I
Easier to reason about
I
Raised abstraction level Easier to avoid
I
I I I I
I
19
Race conditions Deadlocks Starvation Live locks
Location Transparency, configuration-driven (adaptive) deployment
Basic Actor
1
case object Tick
2 3 4
class Counter extends Actor { var counter = 0 //STATE
5
def receive = { //BEHAVIOR case Tick => counter += 1 println(counter) }
6 7 8 9 10 11
20
}
Actor Systems
21
I
Every actor is created within the context of an actor system
I
The actor system is the unit for managing shared facilities like scheduling services, configuration, logging, etc.
I
Several actor systems with different configurations may co-exist within the same JVM instance (there is no global state within Akka itself)
I
There may be millions of actors within a single actor system
Creating Actors
I 1
I 1
I
22
Create an actor system: val system = ActorSystem()
Create an actor: val counter = system.actorOf(Props[Counter], "my-counter")
counter is an ActorRef
You can’t instantiate an Actor directly
I 1 2 3
4
5
6
7
23
This will throw an Exception: scala> new Counter akka.actor.ActorInitializationException: You cannot create an instance of [Counter] explicitly using the constructor (new). You have to use one of the factory methods to create a new actor. Either use: ’val actor = context.actorOf(Props[MyActor])’ (to create a supervised child actor from within an actor), or ’val actor = system.actorOf(Props[MyActor])’ (to create a top level actor from the ActorSystem), or ...
Send: !
I 1
I
24
Pronounced ”bang”, ”tell” or ”fire and forget” counter ! Tick
Asynchronous, does not block
Two-and-a-half ways to stop Actors I 1
Stop: system.stop(counter) will stop the actor after it has finished processing its current message
I 1
Send a Poison Pill: counter ! PoisonPill will stop the actor after it has finished processing all previously enqueued messages
I 1
Send a Kill message: counter ! Kill will make the actor fail, where the default supervisor action is to stop it
25
Exercise: A Simple Compute Actor
I I
Create a class ComputeActor that extends Actor Implement the receive method which should print: I
I
26
A message with the length of a string when you receive a String A message with the square of an integer when you receive an Int
I
Create an object Main that extends App
I
Create an actor system
I
Create a ComputeActor
I
Send different types of messages (strings, ints, etc.) to the actor
I
Shut down the actor system
Replying from an Actor
I 1 2 3 4 5 6 7
27
Reply using the sender ActorRef class ComputeActor extends Actor { def receive = { case s: String => sender ! s.length ... } }
The self
I 1
28
self is an ActorRef to be used from inside of the Actor case TickTwice => self ! Tick; self ! Tick
Send: ? and ask Used to receive a reply without involving a mailbox ”?” pronounced ”ask” Returns directly with a Future1 , which allows transformation and forwarding of the reply:
I I I
1 2
import akka.pattern.{ ask, pipe } import akka.util.duration._
3 4 5 6 7 8
Using ”?” and an implicit timeout:
I 1 2
29
1
val msgContext = ... // some addition to reply val future = computeActor.ask("Hello")(2 seconds) future map { case reply: String => ComputeResult(msgContext, reply) } pipeTo sender
implicit val timeout: Timeout = 2 seconds val future = computeActor ? "Hello"
more on Futures later
Forwarding
1 2
class Router extends Actor { def nextActor: ActorRef = ...
3
def receive = { case message => nextActor forward message }
4 5 6 7 8
I
30
}
forward maintains original sender reference
Exercise: Add replies to ComputeActor
I
Reply to the sender with the computation result
I
Enable the duration DSL using the following import:
1
I
Add a test using ”ask” with an explicit timeout
I
Add a test using ”?” with an implicit timeout
I
Hint: await and obtain the result of a future using
1
I
31
import akka.util.duration._
Await.result(future, timeout)
Run using sbt or Eclipse
Agenda
Why Akka?
Actors
Futures
Testing Actor Systems
32
Future and Promise
33
I
A Future is a read-handle to a single value (read-many) that may become available
I
A Promise is a write-handle to a single value (write-once) that should be made available
Blocking behaviour
Two blocking threads
What we really want
Using Futures with Actors 1 2 3
implicit val timeout: Timeout = 2 seconds // returns a future val future = computeActor ? GetAllResults
4 5 6 7 8
I
1 2 3 4 5
37
// do something asynchronously future map { case result: String => WrappedResult(result) } pipeTo nextStage
All operations creating futures need to know where callbacks are to be executed, described by an ExecutionContext import context.dispatcher // when inside an actor // OR implicit val ec = system.dispatcher // ActorSystem // OR implicit val ec = ExecutionContext.fromExecutor(...)
Future API: Monadic Operations
1 2 3 4
38
def def def def
map[S](f: T => S): Future[S] flatMap[S](f: T => Future[S]): Future[S] filter(p: T => Boolean): Future[T] foreach[U](f: T => U): Unit
Functional Composition
1 2 3 4 5 6 7 8 9
39
val rateQuote = Future { connection.getCurrentValue(USD) } val purchase = rateQuote map { quote => if (isProfitable(quote)) connection.buy(amount, quote) else throw new Exception("not profitable") }
Futures and For-Expressions
I
1
2
To enable for-expressions, futures also have flatMap, filter and foreach combinators val usdQuote = Future { connection.getCurrentValue(USD) } val chfQuote = Future { connection.getCurrentValue(CHF) }
3 4 5 6 7 8
40
val purchase = for { usd ...) Future.fold(futures)(0)((x, y) => ...)
Transformations: recover
1 2 3 4 5
I
44
val purchase: Future[Int] = rateQuote map { quote => connection.buy(amount, quote) } recover { case quoteExc: QuoteChangedException => 0 }
Converts exceptions into results
Transformations: sequence
1 2 3 4
I
45
val stringFutures = for (i Future { GET(url) } }
Transforms a collection[X] to Future[collection[Y]]
Future API
1
package akka.dispatch
2 3 4 5
47
trait Future[+T] extends Awaitable[T] { ... }
Awaitable[T] and Await 1
package akka.dispatch
2 3 4 5 6 7 8
object Await { // Blocks the current thread to wait for the given // Awaitable to be ready, returning // the Awaitable’s result def result[T](awaitable: Awaitable[T], atMost: Duration): T
9
// Blocks the current thread to wait for the given // Awaitable to be ready def ready[T println(s(i)) } def s(a: Any) = a.toString }
7 8 9
val actorRef = TestActorRef(new MyActor)(system) val actor: MyActor = actorRef.underlyingActor
10 11 12 13 14
54
// check business logic actor.s(1) must beEqualTo("1") // check behavior actor.receive.isDefinedAt("not defined") must beEqualTo(false)
TestKit with ScalaTest 1 2 3 4
import import import import
akka.testkit.{ TestKit, ImplicitSender } akka.actor.{ ActorSystem, Props } org.scalatest.WordSpec org.scalatest.junit.JUnitRunner
5 6 7 8
@org.junit.runner.RunWith(classOf[JUnitRunner]) class ActorsSpec extends TestKit(ActorSystem()) with ImplicitSender with WordSpec {
9
"A ComputeActor" should { "respond with the length of a string" in { val ref = system.actorOf(Props[ComputeActor]) ref ! "Hello world" expectMsg(11) } }
10 11 12 13 14 15 16 17
55
}
TestKit with Specs2 1 2 3 4
import import import import
akka.testkit.{ TestKit, ImplicitSender } akka.actor.{ ActorSystem, Props } org.specs2.mutable.Specification org.specs2.time.{ NoTimeConversions => NTC }
5 6 7
class ActorsSpec extends TestKit(ActorSystem()) with ImplicitSender with Specification with NTC {
8
"A ComputeActor" should { "respond with the length of a string" in { val ref = system.actorOf(Props[ComputeActor]) ref ! "Hello world" expectMsg(11) done // necessary because Specs2 wants a matcher } }
9 10 11 12 13 14 15 16 17
56
}
Exercise: TestActorRef and TestKit
I
Extract the computation logic of the ComputeActor class into two compute methods
I
Use a TestActorRef to test the compute methods
I
Using TestKit assertions check that a ComputeActor replies correctly to requests
I
TestKit assertions that might be useful (check out akka.io documentation for more):
1 2
57
expectMsg[T](obj: T): T expectMsgType[T: Manifest]
Test Probe
I 1 2 3 4 5 6 7 8 9 10 11 12
58
Insert test actors in order to verify message flow class ComputeActor extends Actor { def receive = { // ... case s: String => sender ! compute(s) } } val probe = TestProbe() ... // pass desired sender ActorRef explicitly computeActor.tell("Hello world", probe.ref) probe.expectMsg(11)
Exercise: Test Probe and More TestKit Assertions
59
I
Write an actor which upon reception of a Query shall fire off a DbRequest to an actor which has been passed to it in a constructor argument; the reply shall go back to the original sender, wrapped in a Response(rsp: Any).
I
Implement a test case which verifies this process, mocking the DB actor using a TestProbe.
I
Now change the implementation of the actor by adding/removing the use of futures (depending on whether you used them in step 1 or not).
Exercise: Accumulating Computation Results
60
I
Extend the previous exercise to include a second service to be queried in addition to the DB actor and use the auto-pilot feature of the TestProbes to automate the mocked response generation.
I
Include sleep statements simulating response latency, 1 second each, and verify that the combined result is received within 1.5 seconds.
Copyright (c) 2011, 2012 Typesafe, Inc. All rights reserved.
Unless otherwise agreed, training materials may only be used for educational and reference purposes by individual named participants in a training course offered by Typesafe or a Typesafe training partner. Unauthorized reproduction, redistribution, or use of this material is prohibited.
61
View more...
Comments