Async

React

instead of waiting for better times

Johan Andrén

johan.andren@mejsla.se
@apnylle
In the olden days...
... there was a way to write
web applications

that bound 1 thread per request

oh, wait!
let me fix that for

me
Most modern web
frameworks
do bind 1 thread per request
do bind 1 thread per request
t1
Request
Thread
pool

t1
ReSponse
What does that thread spend
most of its time doing?
Hint:
Wait
Request

YOur logic

Blocked

yOur logic

ReSponse

WS
Why is this a problem?

• threadpool depletion
• server choked with 5% cpu usage
• unrelated requests affected
Async!
nodejs

All kinds
of Cool Shit
you never heard
about
WUT!?
Let someone invoke our logic
when a resource is available
What is a resource?
• network

• disk

• internal state
Don´t call us
We´ll call you
Our logic

WS
Async
HTTP-client
Our logic
What do we need?

• callback based clients/drivers/libs
• framework support
but doesn’t this lead to...
”Callback Hell”?

1 GMaps.geocode({!
2
address: fromAddress,!
3
callback: function( results, status ) {!
4
if ( status == "OK" ) {!
5
fromLatLng = results[0].geometry.location;!
6
GMaps.geocode({!
7
address: toAddress,!
8
callback: function( results, status ) {!
9
if ( status == "OK" ) {!
10
toLatLng = results[0].geometry.location;!
11
map.getRoutes({!
12
origin: [ fromLatLng.lat(), fromLatLng.lng() ],!
13
destination: [ toLatLng.lat(), toLatLng.lng() ],!
14
travelMode: "driving",!
15
unitSystem: "imperial",!
16
callback: function( e ){!
17
console.log("ANNNND FINALLY here's the directions..
18
// do something with e!
19
}!
20
});!
21
}!
22
}!
23
});!
24
}!
25
}!
26 });

Not with better abstractions!

• futures / promises
• actors
Future[T]
Empty

Completed

(

Failed )

t
Composition
Future[A]

Future[B]
Map

When A arrives
Apply this A
B transformation
Play action
1 package controllers!
2 !
3 import play.api.mvc._!
4 !
5 object LuckyNumberController extends Controller {!
6 !
7
def giveMeLuckyNumber = Action {!
8
!
9
Ok(views.html.luckyNumber(42))!
10!
11
}!
12 !
13 }
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

package controllers!
!
import scala.concurrent.Future!
import play.api.libs.ws._!
import play.api.libs.concurrent.Execution.Implicits.defaultContext!
import play.api.mvc._!
!
object LuckyNumberController extends Controller {!
!
def giveMeLuckyNumber = Action {!
!
val fResponse: Future[Response] = !
WS.url("http://lucky-number.api/lucky-number").get!
!
!
!
!
!
!
!
!
}!
!
}

Will start execute here
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

package controllers!
!
import scala.concurrent.Future!
import play.api.libs.ws._!
import play.api.libs.concurrent.Execution.Implicits.defaultContext!
import play.api.mvc._!
!
object LuckyNumberController extends Controller {!
!
def giveMeLuckyNumber = Action {!
!
val fResponse: Future[Response] = !
WS.url("http://lucky-number.api/lucky-number").get!
!
!
!
!
!
!
!
!
}!
!
}

THe future response
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

package controllers!
!
import scala.concurrent.Future!
import play.api.libs.ws._!
import play.api.libs.concurrent.Execution.Implicits.defaultContext!
import play.api.mvc._!
!
object LuckyNumberController extends Controller {!
!
def giveMeLuckyNumber = Action {!
!
val fResponse: Future[Response] = WS.url("http://lucky-number.api/lucky-number").get!
!
val fNumber: Future[Int] = fResponse.map { response: Response =>!
Integer.parseInt(response.body)!
}!
!
!
!
!
}!
!
}

When it arrives: do this
Response
Int Transformation
Future[T]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

package controllers!
!
import scala.concurrent.Future!
import play.api.libs.ws._!
import play.api.libs.concurrent.Execution.Implicits.defaultContext!
import play.api.mvc._!
!
object LuckyNumberController extends Controller {!
!
def giveMeLuckyNumber = Action {!
!
val fResponse: Future[Response] = WS.url("http://lucky-number.api/lucky-number").get!
!
val fNumber: Future[Int] = fResponse.map { response: Response =>!
Integer.parseInt(response.body)!
}!
!
!
!
!
}!
!
}

(On this Execution context)
Future[T]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

package controllers!
!
import scala.concurrent.Future!
import play.api.libs.ws._!
import play.api.libs.concurrent.Execution.Implicits.defaultContext!
import play.api.mvc._!
!
object LuckyNumberController extends Controller {!
!
def giveMeLuckyNumber = Action {!
!
val fResponse: Future[Response] = WS.url("http://lucky-number.api/lucky-number").get!
!
val fNumber: Future[Int] = fResponse.map { response: Response =>!
Integer.parseInt(response.body)!
}!
!
fNumber.map { number =>!
Ok(views.html.luckyNumber(number))!
}!
}!
!
}

When it arrives: do this
Transformation
Future[T]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

package controllers!
!
import scala.concurrent.Future!
import play.api.libs.ws._!
import play.api.libs.concurrent.Execution.Implicits.defaultContext!
import play.api.mvc._!
!
object LuckyNumberController extends Controller {!
!
def giveMeLuckyNumber = Action.async {!
!
val fResponse: Future[Response] = WS.url("http://lucky-number.api/lucky-number").get!
!
val fNumber: Future[Int] = fResponse.map { response: Response =>!
Integer.parseInt(response.body)!
}!
!
fNumber.map { number =>!
Ok(views.html.luckyNumber(number))!
}!
}!
!
}

Play allows us to return a
Future[Result]
If there was an exception
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

Future[T]

package controllers!
!
import scala.concurrent.Future!
import play.api.libs.ws._!
import play.api.libs.concurrent.Execution.Implicits.defaultContext!
import play.api.mvc._!
!
object LuckyNumberController extends Controller {!
!
def giveMeLuckyNumber = Action.async {!
!
val fResponse: Future[Response] = WS.url("http://lucky-number.api/lucky-number").get!
!
val fNumber: Future[Int] = fResponse.map { response: Response =>!
Integer.parseInt(response.body)!
}!
!
fNumber.map { number =>!
Ok(views.html.luckyNumber(number))!
}!
}!
!
}
Future[T]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

package controllers!
!
import play.api.libs.ws._!
import play.api.libs.concurrent.Execution.Implicits.defaultContext!
import play.api.mvc._!
!
object LuckyNumberController extends Controller {!
!
def giveMeLuckyNumber = Action.async {!
for {!
response <- WS.url("http://lucky-number.api/lucky-number").get!
} yield {!
val number = Integer.parseInt(response.body)!
Ok(views.html.luckyNumber(number))!
}!
}!
!
}
Actors
State
Inbox
Behaviour
• no work - no thread
• react - no waiting
• can have private state
Actor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

package controllers!
!
import akka.actor.Actor!
!
case object GiveMeNumber!
case class HereYaGo(number: Int)!
!
class LuckyNumberGenerator extends Actor {!
!
var nextNumber = 42!
!
def receive = {!
case GiveMeNumber =>!
sender ! HereYaGo(nextNumber)!
nextNumber += 1!
!
}!
}
Actor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

package controllers!
!
import akka.actor.Actor!
!
case object GiveMeNumber!
case class HereYaGo(number: Int)!
!
class LuckyNumberGenerator extends Actor {!
!
var nextNumber = 42!
!
def receive = {!
case GiveMeNumber =>!
sender ! HereYaGo(nextNumber)!
nextNumber += 1!
!
}!
}

state (mutable)
Actor
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

package controllers!
!
import akka.actor.Actor!
!
case object GiveMeNumber!
case class HereYaGo(number: Int)!
!
class LuckyNumberGenerator extends Actor {!
!
var nextNumber = 42!
!
def receive = {!
case GiveMeNumber =>!
sender ! HereYaGo(nextNumber)!
nextNumber += 1!
!
}!
}

for each message
Controller (calling actor)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

package controllers!
!
import akka.actor._!
import akka.pattern.ask!
import play.api.libs.concurrent.Execution.Implicits.defaultContext!
import play.api.mvc._!
!
class LuckyNumberController(numberGenerator: ActorRef) extends Controller {!
!
def giveMeLuckyNumber = Action.async {!
for {!
response <- (numberGenerator ? GiveMeNumber).mapTo[HereYaGo]!
} yield {!
Ok(views.html.luckyNumber(response.number))!
}!
}!
!
}

Di (someone gave it to us)
Controller (calling actor)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

package controllers!
!
import akka.actor._!
import akka.pattern.ask!
import play.api.libs.concurrent.Execution.Implicits.defaultContext!
import play.api.mvc._!
!
class LuckyNumberController(numberGenerator: ActorRef) extends Controller {!
!
def giveMeLuckyNumber = Action.async {!
for {!
response <- (numberGenerator ? GiveMeNumber).mapTo[HereYaGo]!
} yield {!
Ok(views.html.luckyNumber(response.number))!
}!
}!
!
}

Sends message, gives us
Future[Any] (reply)
Controller (calling actor)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

package controllers!
!
import akka.actor._!
import akka.pattern.ask!
import play.api.libs.concurrent.Execution.Implicits.defaultContext!
import play.api.mvc._!
!
class LuckyNumberController(numberGenerator: ActorRef) extends Controller {!
!
def giveMeLuckyNumber = Action.async {!
for {!
response <- (numberGenerator ? GiveMeNumber).mapTo[HereYaGo]!
} yield {!
Ok(views.html.luckyNumber(response.number))!
}!
}!
!
}

We know what we will get back
Controller (calling actor)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

package controllers!
!
import akka.actor._!
import akka.pattern.ask!
import play.api.libs.concurrent.Execution.Implicits.defaultContext!
import play.api.mvc._!
!
class LuckyNumberController(numberGenerator: ActorRef) extends Controller {!
!
def giveMeLuckyNumber = Action.async {!
for {!
response <- (numberGenerator ? GiveMeNumber).mapTo[HereYaGo]!
} yield {!
Ok(views.html.luckyNumber(response.number))!
}!
}!
!
}

Make a webpage out of that
What to look out for

• really heavy computations
• blocking by miss-take
• jdbc :(
I wanna try it!
www.playframework.com
typesafe.com
Qs?
K Thx Bye!
Johan Andrén

johan.andren@mejsla.se
@apnylle

Async – react, don't wait