Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Why Being Reactive is Good

Why Being Reactive is Good

One of the more difficult aspects of systems architecture is finding concrete terms to describe desirable characteristics that everyone understands. Reactive was one of those terms until two years ago, when the Reactive Manifesto was released. Today, over 12,000 people have signed the online document and participated in its evolution. But what does it mean, and how do the principles described by it help developers engineer systems that are elastic, resilient and responsive to users in the face of bursty load and random/unknown failures? We will discuss these topics, as well as clearly discuss the concepts of asynchronous tasks, non-blocking threads, concurrency issues and parallelism.

The goal of this talk is to frame the term reactive in a positive light, because when it comes to building real-time systems, being reactive is good.

Kevin Webber

October 22, 2015
Tweet

More Decks by Kevin Webber

Other Decks in Programming

Transcript

  1. Being Reactive is Good
    Kevin Webber, Developer Advocate, Typesafe

    View Slide

  2. View Slide

  3. View Slide

  4. View Slide

  5. View Slide

  6. View Slide

  7. View Slide

  8. View Slide

  9. View Slide

  10. View Slide

  11. Problem?
    » we used the right tools to build the wrong thing

    View Slide

  12. Problem?
    » we used the right tools to build the wrong thing
    » proactive isn't always good

    View Slide

  13. Problem?
    » we used the right tools to build the wrong thing
    » proactive isn't always good
    » reactive isn't always bad

    View Slide

  14. View Slide

  15. View Slide

  16. View Slide

  17. View Slide

  18. » let principles guide our
    » design
    » tool selection
    » implementation choices

    View Slide

  19. » let principles guide our
    » design
    » tool selection
    » implementation choices
    » principles should be accessible and universal

    View Slide

  20. » let principles guide our
    » design
    » tool selection
    » implementation choices
    » principles should be accessible and universal
    » principles should help engineers
    » meet expectations
    » take advantage of the latest advancements in
    technology

    View Slide

  21. What is Reactive?

    View Slide

  22. asynchronous, non-blocking, real-time, highly-
    available, loosely coupled, scalable, fault-tolerant,
    concurrent, reactive, event-driven, push instead of
    pull, distributed, low latency, high throughput...

    View Slide

  23. asynchronous, non-blocking, real-time, highly-
    available, loosely coupled, scalable, fault-tolerant,
    concurrent, reactive, event-driven, push instead of
    pull, distributed, low latency, high throughput...
    Too complicated. We need a simple vocabulary.

    View Slide

  24. View Slide

  25. View Slide

  26. View Slide

  27. View Slide

  28. Typesafe
    Reactive
    Platform
    » Play: RESTful API framework
    » Akka: Distributed computing
    framework
    » Spark: General purpose in-
    memory compute engine

    View Slide

  29. Akka Users

    View Slide

  30. Message-driven
    » distribution
    » location transparency
    » isolation

    View Slide

  31. object GameEngineActor {
    def props = Props(new GameEngineActor)
    }
    }

    View Slide

  32. object GameEngineActor {
    def props = Props(new GameEngineActor)
    }
    class GameEngineActor extends Actor {
    val log = Logging(context.system, this)
    var openGameQ = new Queue[ActorRef]
    }

    View Slide

  33. object GameEngineActor {
    def props = Props(new GameEngineActor)
    }
    class GameEngineActor extends Actor {
    val log = Logging(context.system, this)
    var openGameQ = new Queue[ActorRef]
    def receive = {
    // find open game and register player for the game
    case r: RegisterPlayerRequest => findGame(r) ! r
    case _ => log.error(_)
    }
    }

    View Slide

  34. object GameEngineActor {
    def props = Props(new GameEngineActor)
    }
    class GameEngineActor extends Actor {
    val log = Logging(context.system, this)
    var openGameQ = new Queue[ActorRef]
    def receive = {
    // find open game and register player for the game
    case r: RegisterPlayerRequest => findGame(r) ! r
    case _ => log.error(_)
    }
    private def findGame(r: RegisterPlayerRequest): ActorRef = {
    if (openGameQ.isEmpty) {
    // create a new game and add it to the queue
    val newGame = {
    val gameUuid = java.util.UUID.randomUUID.toString
    context.actorOf(Props[GameActor], name = "gameActor" + gameUuid)
    }
    openGameQ += newGame
    newGame
    } else {
    // pull a game off the queue that is waiting for players
    openGameQ.dequeue
    }
    }
    }

    View Slide

  35. Resilient
    » supervision

    View Slide

  36. Resilient
    » emitting errors

    View Slide

  37. Resilient
    » handling errors

    View Slide

  38. Resilient
    » dedicated separate error
    channel

    View Slide

  39. Elastic
    » scale up
    » async
    » non-blocking

    View Slide

  40. Elastic
    » scale up
    » async
    » non-blocking
    » scale out
    » immutable data
    » share nothing

    View Slide

  41. Responsive
    » responsive to events
    » responsive to load
    » responsive to failure
    » responsive to users

    View Slide

  42. » responsive
    a request for service is always answered, even
    when failures occur.
    » elastic
    services scale up and down on demand.
    » resilient
    services recover from failures.
    » message driven
    services respond to the world, not attempt to
    control what it does.

    View Slide

  43. How can we envision Proactive Security Solutions as
    Reactive Security Solutions?
    Using:
    » Reactive principles
    » The Typesafe Reactive Platform

    View Slide

  44. View Slide

  45. How do we get there?
    Applying the Reactive principles to build a fully
    reactive microservices system, using:
    » The Typesafe Reactive Platform
    » Third-party tools
    » Existing infrastructure (hybrid-cloud, etc)

    View Slide

  46. View Slide

  47. Async in Action with the
    Play framework

    View Slide

  48. Play Anatomy
    » Stateless: session is stored in the browser cookie
    » Actions: user-defined functions that run in a
    different context than the request loop

    View Slide

  49. Play at its core
    » RESTful API framework

    View Slide

  50. Play at its core
    » RESTful API framework
    » Familiar MVC paradigm

    View Slide

  51. Play at its core
    » RESTful API framework
    » Familiar MVC paradigm
    » Embraces flows of data
    » WebSockets, SSE
    » File uploads

    View Slide

  52. Play at its core
    » RESTful API framework
    » Familiar MVC paradigm
    » Embraces flows of data
    » WebSockets, SSE
    » File uploads
    » Integration with Akka
    » Distribute work via messaging

    View Slide

  53. Single thread per request

    View Slide

  54. Single thread per request
    » A thread keeps data in memory until it has access
    to the CPU

    View Slide

  55. Single thread per request
    » A thread keeps data in memory until it has access
    to the CPU
    » More threads = more memory required

    View Slide

  56. Single thread per request
    » A thread keeps data in memory until it has access
    to the CPU
    » More threads = more memory required
    » If a thread is idle while blocked (e.g, IO)
    » Memory is being consumed inefficiently
    » Must limit total number of threads
    » This can leave the CPU idle (bounded on memory)

    View Slide

  57. Work stealing

    View Slide

  58. » Play uses a fork join execution context with work
    stealing
    » Does not bind a thread to a request

    View Slide

  59. » Play uses a fork join execution context with work
    stealing
    » Does not bind a thread to a request
    » Uses Futures and Thread Pools to let CPU go at
    full throttle

    View Slide

  60. » Play uses a fork join execution context with work
    stealing
    » Does not bind a thread to a request
    » Uses Futures and Thread Pools to let CPU go at
    full throttle
    » Uses NIO.2 through Netty to avoid blocking on
    network IO

    View Slide

  61. » Play uses a fork join execution context with work
    stealing
    » Does not bind a thread to a request
    » Uses Futures and Thread Pools to let CPU go at
    full throttle
    » Uses NIO.2 through Netty to avoid blocking on
    network IO
    » Stateless (no HTTP session)

    View Slide

  62. » Play uses a fork join execution context with work
    stealing
    » Does not bind a thread to a request
    » Uses Futures and Thread Pools to let CPU go at
    full throttle
    » Uses NIO.2 through Netty to avoid blocking on
    network IO
    » Stateless (no HTTP session)
    » No ThreadLocal anywhere

    View Slide

  63. Future[Result]
    def index = Action.async {
    implicit request =>
    Ok(...)
    }

    View Slide

  64. Future[Result]
    » The async block indicates that the controller will
    return a Future[Result]

    View Slide

  65. Future[Result]
    » The async block indicates that the controller will
    return a Future[Result]
    » The Future provides a way to do asynchronous
    handling but doesn't necessarily have to be non-
    blocking

    View Slide

  66. Future[Result]
    » The async block indicates that the controller will
    return a Future[Result]
    » The Future provides a way to do asynchronous
    handling but doesn't necessarily have to be non-
    blocking
    » If a thread can't be deallocated while waiting for
    those other systems to respond, then it is
    blocking

    View Slide

  67. Future[Result]
    » The async block indicates that the controller will
    return a Future[Result]
    » The Future provides a way to do asynchronous
    handling but doesn't necessarily have to be non-
    blocking
    » If a thread can't be deallocated while waiting for
    those other systems to respond, then it is
    blocking
    » Order of execution doesn't matter

    View Slide

  68. View Slide

  69. View Slide

  70. Implications
    » ACID doesn't work across location/trust boundaries
    » No 2-Phase commits (2PC)
    » No holding locks for the duration of the work

    View Slide

  71. Implications
    » Must embrace eventual consistency
    » Multiple workflows across multiple systems
    » Each provides a path of compensating actions

    View Slide

  72. Saga pattern
    » a Saga splits work into individual transactions
    » effects can be reversed after work has been
    performed and commited

    View Slide

  73. ConductR and
    Monitoring

    View Slide

  74. View Slide

  75. View Slide

  76. How do I get started?
    https://www.typesafe.com/activator/download

    View Slide

  77. View Slide

  78. Thank you!
    Kevin Webber
    Medium: medium.com/@kvnwbbr
    Twitter: @kvnwbbr
    Point of contact
    [email protected]

    View Slide