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

Practical RxJava workshop

Simon Baslé
February 08, 2016

Practical RxJava workshop

v2 of the Practical RxJava workshop, first presented at JFokus 2016

Simon Baslé

February 08, 2016
Tweet

More Decks by Simon Baslé

Other Decks in Programming

Transcript

  1. Practical RxJava

    View Slide

  2. @Couchbase @InfoQFR @bbl_fr
    @SimonBasle

    View Slide

  3. the
    Plan
    &
    Goals

    View Slide

  4. RxJava
    101

    View Slide

  5. migrate a
    Legacy
    application
    RxJava
    101

    View Slide

  6. migrate a
    Legacy
    application
    learn
    Operators
    of interest
    RxJava
    101

    View Slide

  7. migrate a
    Legacy
    application
    learn
    Operators
    of interest
    9 steps
    +
    Q&
    A
    RxJava
    101

    View Slide

  8. Why?

    View Slide

  9. Blocking
    it’s
    Evil, m’kay?

    View Slide

  10. Blocking
    it’s
    Evil, m’kay?

    View Slide

  11. we need
    asynchronous code

    View Slide

  12. we need
    reactive
    asynchronous code

    View Slide

  13. we need
    parallelisable
    asynchronous code

    View Slide

  14. we need
    composable
    asynchronous code

    View Slide

  15. we need
    readable
    asynchronous code

    View Slide

  16. but
    how
    ?

    View Slide

  17. Futures?
    Callbacks?

    View Slide

  18. Futures?
    Callbacks?
    too easy to block
    (get)
    complex beyond 1
    level of composition
    no composition
    have you heared of
    Callback Hell?

    View Slide

  19. DocumentService.find("userId", new Callback>() {
    public void onSuccess(List result) {
    final List jsonList = new ArrayList(10);
    int taken = 0;
    for (Document doc : result) {
    if (taken >= 10)
    break;
    if (!doc.isStarred())
    continue;
    taken++;
    final CountDownLatch rendezVous = new CountDownLatch(3);
    final JsonObject jsonBuffer = new JsonObject();
    jsonBuffer.appendInt("id", doc.getId());
    jsonBuffer.append("text", doc.getText());
    CommentService.findForDoc(doc, new Callback>() {
    public void onSuccess(List comments) {
    final JsonObject commentArray = JsonObject.createArray();
    CountDownLatch userLatch = new CountDownLatch(comments.size());
    for (Comment c : comments) {
    JsonObject cj = new JsonObject();
    cj.append("content", c.getText());
    cj.append("date", c.getDate());
    UserService.find(c.getUserId(), new Callback() {
    public void onSuccess(User user) {
    cj.append("author", user.getName());
    cj.append("nickname", user.getLogin());
    Futures?
    Callbacks?
    too easy to block
    (get)
    complex beyond 1
    level of composition
    no composition
    have you heared of
    Callback Hell?

    View Slide

  20. RxJava
    101

    View Slide

  21. RxJava
    101

    View Slide

  22. Netflix
    OpenSource

    View Slide

  23. dual of
    Iterable - Iterator

    View Slide

  24. Iterable - Iterator
    becomes
    Observable - Observer

    View Slide

  25. Iterable - Iterator
    becomes
    Observable - Observer
    “Pull”
    “Push”

    View Slide

  26. compose

    View Slide

  27. compose
    asynchronous programs
    based on events

    View Slide

  28. compose
    asynchronous programs
    based on events
    using observable sequences

    View Slide

  29. the Reactive Manifesto
    reactivemanifesto.org

    View Slide

  30. reactive-streams.org
    standardize on the jvm

    View Slide

  31. reactive-streams.org
    standardize on the jvm
    akka - reactor - rxjava

    View Slide

  32. Show Me
    how it works !

    View Slide

  33. interface Observer

    View Slide

  34. interface Observer
    onNext(T data)

    View Slide

  35. interface Observer
    onNext(T data)
    onCompleted()

    View Slide

  36. interface Observer
    onNext(T data)
    onCompleted()
    onError(Throwable t)

    View Slide

  37. interface Observer
    onNext(T data)
    onCompleted()
    onError(Throwable t)

    View Slide

  38. Observable

    View Slide

  39. Observable
    compose & chain a stream

    View Slide

  40. Observable
    subscribe an Observer

    View Slide

  41. so much
    Choice, you’ll see!

    View Slide

  42. still lost…
    time to practice

    View Slide

  43. The
    Legacy app

    View Slide

  44. Wow
    pool API?
    Much Legacy!

    View Slide

  45. Doge Mining Pool Client (UI?)
    Rest Controller Rest Controller Rest Controller Rest Controller
    Service Service Service
    External API External API External API
    DB
    L
    E
    G
    A
    C
    Y

    View Slide

  46. Doge Mining Pool Client (UI?)
    Rest Controller Rest Controller Rest Controller Rest Controller
    Service Service Service
    External API External API External API
    DB

    View Slide

  47. Doge Mining Pool Client (UI?)
    Rest Controller Rest Controller Rest Controller Rest Controller
    Service Service Service
    External API External API External API
    DB

    View Slide

  48. simple
    Creation
    1

    View Slide

  49. Migrate Services
    ● AdminService
    ● CoinService
    ● HashrateService
    ● PoolService

    View Slide

  50. Useful Operators
    ● Observable.just
    ● Observable.from
    ● Observable.create (try it on PoolService.connect)
    ● map

    View Slide

  51. Useful Operators
    ● Observable.just
    ● Observable.from
    ● Observable.create
    ● map

    View Slide

  52. But now it doesn’t compile
    how to adapt the Controllers?

    View Slide

  53. Naively
    Block on an Observable
    take(n) vs
    single() vs
    first()
    xxxOrDefault(t), toList()

    View Slide

  54. Take

    View Slide

  55. Single

    View Slide

  56. SingleOrDefault

    View Slide

  57. Transform
    2

    View Slide

  58. Migrate Services
    ● PoolRateService

    View Slide

  59. Useful Operators
    ● flatMap
    ● reduce

    View Slide

  60. flatMap

    View Slide

  61. reduce

    View Slide

  62. Filter
    3

    View Slide

  63. Migrate Services
    ● UserService
    ○ findAll for now naively adapted (Observable.from)
    ○ compose on findAll for getUser / getUserByLogin
    ● SearchService

    View Slide

  64. Useful Operators
    ● filter
    ● take
    ● flatMap
    to asynchronously retrieve additional data needed for filter

    View Slide

  65. filter

    View Slide

  66. Count
    4

    View Slide

  67. Migrate Services
    RankingService
    ○ use from for rankByHashrate / rankByCoins
    ○ complete migration for other methods

    View Slide

  68. Useful Operators
    ● takeUntil
    ● count
    ● take

    View Slide

  69. takeUntil

    View Slide

  70. count

    View Slide

  71. Time for a
    Break
    :)

    View Slide

  72. Side Effects
    5

    View Slide

  73. doOnXXX
    ● doOnNext
    ● doOnError
    ● doOnCompleted
    ● doOnEach

    View Slide

  74. Migrate Services
    PoolService
    ○ add a line of log each time a user connects

    View Slide

  75. Combine
    6

    View Slide

  76. Level: Simple
    Concat

    View Slide

  77. Level: Intermediate
    Merge

    View Slide

  78. Level: Advanced
    Zip

    View Slide

  79. Migrate Services
    StatService.getAllStats
    ● for each User
    ○ retrieve his hashrate
    ○ retrieve how many coins he mined
    ○ combine both and make a UserStat

    View Slide

  80. Live &
    Let Die and Retry
    oo
    7

    View Slide

  81. The Problem
    StatService.lastBlockFoundBy
    ○ Has intermittent bug that causes it to crash with an
    IndexOutOfBoundsException
    ○ We’d like to retry the call when this happens, to
    prevent the error.

    View Slide

  82. The Plan
    Make the method Observable (generate in a defer, log
    in a doOnNext, pick the user via flatMap and
    elementAt…)
    Migrate code as before, it still randomly fails
    Make it automatically retry

    View Slide

  83. The Plan (how to test)
    Edit the PoolController.lastBlock() method so it doesn’t catch and
    recover
    Execute PoolControllerTest.testLastBlock() and verify it
    succeeds
    Sometimes it should print several “ELECTED” messages, this is the
    retry in action

    View Slide

  84. Useful Operators
    ● defer
    ● flatMap
    ● elementAt
    ● retry

    View Slide

  85. ElementAt

    View Slide

  86. Retry

    View Slide

  87. Chain
    8

    View Slide

  88. Migrate Services
    ExchangeRateService
    ○ calls two external APIs: doge-to-dollar rate and
    currency-to-currency exchange rate
    ○ two chained REST calls
    ○ combination of the two gives doge-to-anyCurrency

    View Slide

  89. Useful Operators
    ● Observable.create
    ○ wrap the REST call on each subscription
    ○ catch RestClientException and make them into
    DogePoolException (especially for timeouts)

    View Slide

  90. Useful Operators
    ● zipWith
    ○ similar to zip but called from stream A instead of
    statically
    ○ combine both rates to get the final one

    View Slide

  91. Oh
    Hey!
    ! WARNING - Incoming Product Owner !

    View Slide

  92. "this free exchange rate API crashes too
    often,
    make it so we switch to an alternative
    paid APi when it’s the case"
    - the Product Owner

    View Slide

  93. "this free exchange rate API crashes too
    often,
    make it so we switch to an alternative
    paid APi when it’s the case"
    - the Product Owner
    !

    View Slide

  94. "oh yeah and if you could go on and
    track costs of these calls
    it would be great"
    - the Product Owner, pouring himself a coffee
    Much Surprise
    Such Requirements
    wow

    View Slide

  95. The Plan
    ● AdminService
    ○ add method+variable to track cost for this month
    ● ExchangeRateService
    ○ rate retrieval method similar to current one
    ○ use endpoint from exchange.nonfree.api.baseUrl
    ● switch thanks to OnErrorResumeNext
    ● use side effects
    ○ log when we switch & track the cost

    View Slide

  96. OnErrorResumeNext

    View Slide

  97. OnErrorReturn

    View Slide

  98. Clean-up Controllers
    9

    View Slide

  99. Asynchronous Response
    ● prepare a DeferredResult
    ○ that will be returned without blocking
    ● subscribe on stream
    ○ onNext: inject T via setResult
    ○ onError: create a DogePoolException and inject it via
    setErrorResult

    View Slide

  100. This could be Generalized
    ● using ReturnValueHandler and a simple adapter
    of Observable to DeferredResult
    ● each time an Observable is returned, it’ll be
    converted into this adapter
    ○ WebAsyncUtils.getAsyncManager(...).
    startDeferredResultProcessing(...)

    View Slide

  101. Testing
    ● using mockMvc’s andReturn() to get a
    MvcResult
    ○ assert status().isOk() and request().asyncStarted()
    ● trigger the async processing
    ○ mockMvc.perform(asyncDispatch(mvcResult))
    ● from this point assert as before

    View Slide

  102. Migrate Services
    ● AdminController (simple)
    ● IndexController (trickier)
    ○ use zip, flatMap, single… to detect bad users
    ● UserProfileController (idem)

    View Slide

  103. Q&
    A

    View Slide

  104. TakeAway

    View Slide

  105. Nonblocking code

    View Slide

  106. without Callbacks

    View Slide

  107. with a
    minimum of
    visual cluter

    View Slide

  108. readable and
    understandable

    View Slide

  109. Hope
    you loved it!

    View Slide

  110. View Slide

  111. Merci!

    View Slide

  112. ➔ RxJava Logo and Marble Diagrams - ReactiveX Documentation
    http://reactivex.io
    ➔ Cat Attack - Static416
    on Flickr
    ➔ Stormtroopers Series - J.D. Hancock
    http://photos.jdhancock.com/series/stormtroopers.html
    ➔ Blackhole with Corona - NASA
    on Wikimedia
    ➔ Broken Keyboard - Santeri Viinamäki
    on Wikimedia
    ➔ Gobot Toys- Tom Prankerd
    on Wikimedia
    ➔ Coffee Filter - Unsplash
    on Pixabay
    ➔ Abacus - Succo
    on Pixabay
    ➔ Still from Office Space - Copyright, 20th Century Fox
    By
    By-Nc
    By
    Cc0
    By
    By
    Cc0
    Cc0
    ©
    Credits (1/2)

    View Slide

  113. ➔ Hand Reaching - Kinseykick
    on Pixabay
    ➔ Sidecar Motocross - Jean-Daniel Echenard
    on Flickr
    ➔ Twin Falls - Blese
    on Flickr
    ➔ Tumbeast Gnawing on Servers - Matthew Inman
    on Wikimedia
    ➔ Trap - Anticiv
    on Flickr
    ➔ Kane Cleaning Supplies - Collinanderson
    on Flickr
    ➔ Chain - Stomchak
    on Wikimedia
    ➔ Takeaway - Edimburgh Blog
    on Flickr
    ➔ The End Sands - Elektro-Plan
    on Pixabay
    Cc0
    By-Nd
    By-Nc
    By
    By-Sa
    By
    By
    By
    Cc0
    Credits (2/2)

    View Slide