Upload
johan-andren
View
2.304
Download
0
Tags:
Embed Size (px)
DESCRIPTION
Slides from my topconf 2013 talk
Citation preview
AsyncReact instead of waiting for better times
Johan Andrén [email protected] @apnylle
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
Most modern web frameworks
do bind 1 thread per request
do bind 1 thread per request
Request
ReSponse
t1
t1
Thread pool
What does that thread spend most of its time doing?
Hint:
Wait
WS
Request
ReSponse
Blocked
YOur logic
yOur logic
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
• network• disk• internal state
What is a resource?
We´ll call youOur logic
Our logic
WS
Async HTTP-client
Don´t call us
What do we need?
• callback based clients/drivers/libs
• framework support
but doesn’t this lead to...
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”?
Not with better abstractions!
24 }!25 }!26 });
• futures / promises
•actors
Future[T]Empty
Completed t
Failed )(
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 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
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
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
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)
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
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]
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
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 }
Actors
Inbox
Behaviour
State
• no work - no thread
• react - no waiting
• can have private state
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
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)
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
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)
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)
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
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
What to look out for
• really heavy computations
• blocking by miss-take
• jdbc :(
I wanna try it!
www.playframework.comtypesafe.com