Async – react, don't wait

2,863 views
2,687 views

Published on

Slides from my topconf 2013 talk

Published in: Technology, Business
0 Comments
4 Likes
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
2,863
On SlideShare
0
From Embeds
0
Number of Embeds
0
Actions
Shares
0
Downloads
32
Comments
0
Likes
4
Embeds 0
No embeds

No notes for slide

Async – react, don't wait

  1. 1. Async React instead of waiting for better times Johan Andrén johan.andren@mejsla.se @apnylle
  2. 2. 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
  3. 3. Most modern web frameworks do bind 1 thread per request
  4. 4. do bind 1 thread per request t1 Request Thread pool t1 ReSponse
  5. 5. What does that thread spend most of its time doing? Hint:
  6. 6. Wait Request YOur logic Blocked yOur logic ReSponse WS
  7. 7. Why is this a problem? • threadpool depletion • server choked with 5% cpu usage • unrelated requests affected
  8. 8. Async! nodejs All kinds of Cool Shit you never heard about
  9. 9. WUT!? Let someone invoke our logic when a resource is available What is a resource? • network • disk • internal state
  10. 10. Don´t call us We´ll call you Our logic WS Async HTTP-client Our logic
  11. 11. What do we need? • callback based clients/drivers/libs • framework support but doesn’t this lead to...
  12. 12. ”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 }!
  13. 13. 25 }! 26 }); Not with better abstractions! • futures / promises • actors
  14. 14. Future[T] Empty Completed ( Failed ) t
  15. 15. Composition Future[A] Future[B] Map When A arrives Apply this A B transformation
  16. 16. 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 }
  17. 17. 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
  18. 18. 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
  19. 19. 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
  20. 20. 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)
  21. 21. 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
  22. 22. 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]
  23. 23. 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))! }! }! ! }
  24. 24. 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))! }! }! ! }
  25. 25. Actors State Inbox Behaviour
  26. 26. • no work - no thread • react - no waiting • can have private state
  27. 27. 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! ! }! }
  28. 28. 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)
  29. 29. 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
  30. 30. 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)
  31. 31. 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)
  32. 32. 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
  33. 33. 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
  34. 34. What to look out for • really heavy computations • blocking by miss-take • jdbc :(
  35. 35. I wanna try it! www.playframework.com typesafe.com
  36. 36. Qs? K Thx Bye! Johan Andrén johan.andren@mejsla.se @apnylle

×