async – react, don't wait

36
Async React instead of waiting for better times Johan Andrén johan.andren@mejsla.se @apnylle

Upload: johan-andren

Post on 06-May-2015

2.322 views

Category:

Technology


0 download

DESCRIPTION

Slides from my topconf 2013 talk

TRANSCRIPT

Page 1: Async – react, don't wait

AsyncReact instead of waiting for better times

Johan Andrén [email protected] @apnylle

Page 2: Async – react, don't wait

In the olden days...

oh, wait!let me fix that for me

... there was a way to write web applications

that bound 1 thread per request

Page 3: Async – react, don't wait

Most modern web frameworks

do bind 1 thread per request

Page 4: Async – react, don't wait

do bind 1 thread per request

Request

ReSponse

t1

t1

Thread pool

Page 5: Async – react, don't wait

What does that thread spend most of its time doing?

Hint:

Page 6: Async – react, don't wait

Wait

WS

Request

ReSponse

Blocked

YOur logic

yOur logic

Page 7: Async – react, don't wait

Why is this a problem?

• threadpool depletion

• server choked with 5% cpu usage

• unrelated requests affected

Page 8: Async – react, don't wait

Async!nodejs

All kinds of Cool Shit

you never heard about

Page 9: Async – react, don't wait

WUT!?

Let someone invoke our logic when a resource is available

• network• disk• internal state

What is a resource?

Page 10: Async – react, don't wait

We´ll call youOur logic

Our logic

WS

Async HTTP-client

Don´t call us

Page 11: Async – react, don't wait

What do we need?

• callback based clients/drivers/libs

• framework support

but doesn’t this lead to...

Page 12: Async – react, don't wait

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 }!

”Callback Hell”?

Page 13: Async – react, don't wait

Not with better abstractions!

24 }!25 }!26 });

• futures / promises

•actors

Page 14: Async – react, don't wait

Future[T]Empty

Completed t

Failed )(

Page 15: Async – react, don't wait

Composition

Future[A] Future[B]

Map

When A arrives Apply this A ⇒ B transformation

Page 16: Async – react, don't wait

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 }

Page 17: Async – react, don't wait

1 package controllers! 2 ! 3 import scala.concurrent.Future! 4 import play.api.libs.ws._! 5 import play.api.libs.concurrent.Execution.Implicits.defaultContext! 6 import play.api.mvc._! 7 ! 8 object LuckyNumberController extends Controller {! 9 !10 def giveMeLuckyNumber = Action {!11 !12 val fResponse: Future[Response] = !13 WS.url("http://lucky-number.api/lucky-number").get!14 !15 !16 !17 !18 !19 !20 !21 !22 }!23 !24 }

Will start execute here

Page 18: Async – react, don't wait

1 package controllers! 2 ! 3 import scala.concurrent.Future! 4 import play.api.libs.ws._! 5 import play.api.libs.concurrent.Execution.Implicits.defaultContext! 6 import play.api.mvc._! 7 ! 8 object LuckyNumberController extends Controller {! 9 !10 def giveMeLuckyNumber = Action {!11 !12 val fResponse: Future[Response] = !13 WS.url("http://lucky-number.api/lucky-number").get!14 !15 !16 !17 !18 !19 !20 !21 !22 }!23 !24 }

THe future response

Page 19: Async – react, don't wait

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

When it arrives: do this Response ⇒ Int Transformation

Page 20: Async – react, don't wait

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

(On this Execution context)

Page 21: Async – react, don't wait

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

Transformation

Page 22: Async – react, don't wait

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

Future[Result]

Page 23: Async – react, don't wait

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

If there was an exception

Page 24: Async – react, don't wait

Future[T]

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

Page 25: Async – react, don't wait

Actors

Inbox

Behaviour

State

Page 26: Async – react, don't wait

• no work - no thread

• react - no waiting

• can have private state

Page 27: Async – react, don't wait

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

Actor

Page 28: Async – react, don't wait

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

Actor

state (mutable)

Page 29: Async – react, don't wait

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

Actor

for each message

Page 30: Async – react, don't wait

Controller (calling actor) 1 package controllers! 2 ! 3 import akka.actor._! 4 import akka.pattern.ask! 5 import play.api.libs.concurrent.Execution.Implicits.defaultContext! 6 import play.api.mvc._! 7 ! 8 class LuckyNumberController(numberGenerator: ActorRef) extends Controller {! 9 !10 def giveMeLuckyNumber = Action.async {!11 for {!12 response <- (numberGenerator ? GiveMeNumber).mapTo[HereYaGo]!13 } yield {!14 Ok(views.html.luckyNumber(response.number))!15 }!16 }!17 !18 } Di (someone gave it to us)

Page 31: Async – react, don't wait

Controller (calling actor) 1 package controllers! 2 ! 3 import akka.actor._! 4 import akka.pattern.ask! 5 import play.api.libs.concurrent.Execution.Implicits.defaultContext! 6 import play.api.mvc._! 7 ! 8 class LuckyNumberController(numberGenerator: ActorRef) extends Controller {! 9 !10 def giveMeLuckyNumber = Action.async {!11 for {!12 response <- (numberGenerator ? GiveMeNumber).mapTo[HereYaGo]!13 } yield {!14 Ok(views.html.luckyNumber(response.number))!15 }!16 }!17 !18 } Sends message, gives us

Future[Any] (reply)

Page 32: Async – react, don't wait

Controller (calling actor) 1 package controllers! 2 ! 3 import akka.actor._! 4 import akka.pattern.ask! 5 import play.api.libs.concurrent.Execution.Implicits.defaultContext! 6 import play.api.mvc._! 7 ! 8 class LuckyNumberController(numberGenerator: ActorRef) extends Controller {! 9 !10 def giveMeLuckyNumber = Action.async {!11 for {!12 response <- (numberGenerator ? GiveMeNumber).mapTo[HereYaGo]!13 } yield {!14 Ok(views.html.luckyNumber(response.number))!15 }!16 }!17 !18 } We know what we will get back

Page 33: Async – react, don't wait

Controller (calling actor) 1 package controllers! 2 ! 3 import akka.actor._! 4 import akka.pattern.ask! 5 import play.api.libs.concurrent.Execution.Implicits.defaultContext! 6 import play.api.mvc._! 7 ! 8 class LuckyNumberController(numberGenerator: ActorRef) extends Controller {! 9 !10 def giveMeLuckyNumber = Action.async {!11 for {!12 response <- (numberGenerator ? GiveMeNumber).mapTo[HereYaGo]!13 } yield {!14 Ok(views.html.luckyNumber(response.number))!15 }!16 }!17 !18 }

Make a webpage out of that

Page 34: Async – react, don't wait

What to look out for

• really heavy computations

• blocking by miss-take

• jdbc :(

Page 35: Async – react, don't wait

I wanna try it!

www.playframework.comtypesafe.com

Page 36: Async – react, don't wait

Johan Andrén [email protected] @apnylle

K Thx Bye!

Qs?