Chapter 20. reactive-spring (optional): Reactive Programming with Spring

20.1. Introduction

This lab refactors the MVC application to use a microservice.

What you will learn:

  1. How to write a simple Reactive application
  2. When Reactive is faster than non-reactive, when it is not

Specific subjects you will gain experience with:

  1. Spring WebClient
  2. Mono and Flux from Reactor

Estimated time to complete: 25 minutes

20.1.1. Overview

This lab runs a cut-down version of the REST server you wrote in an earlier lab.

Everything else involves running as JUnit tests.

The logging output of the Unit tests is important. A typical log message is shown below. The item in square brackets is the name of the thread producing the output, in this case reactor-http-nio-4. The thread the unit-tests run in is [main].

[reactor-http-nio-4] INFO : rewards.internal.account.Account - Creating new Account …​

20.2. Instructions

20.2.1. Reactive Code Demo

  1. First lets compare some simple Reactive code
  2. Open SimpleReactiveTests.java and run it (TODO-01). It uses both JDK streams and a Reactive Flux.
  3. Notice which threads are used by each part of the test.

20.2.2. Run the Accounts Server

  1. Next run the REST server - this is a Boot project so you can right client and Run As >> Spring Boot App (TODO-02).
  2. Check http://localhost:8080 to see that it is working - in particular the JSON links.
  3. The remaining tests all make RESTful requests from the server.

20.2.3. Fetch an Account using a RestTemplate

  1. TODO-03: Write the code to fetch an account using a RestTemplate. The URL is http://localhost:8080/accounts/0.
  2. Run the test ensure it passes.
  3. The body of the HTTP response contains JSON data that was converted to an Account object by Spring. The Account constructor logs the account creation. Note that it is also using [main] thread.

20.2.4. Fetch an Account using a Web Client

  1. TODO-04: Repeat the fetch, but this time using a WebClient. You will need to create it, configure it, retrieve the data as a Mono<Account>. Use the example in the slides to help you.
  2. Block until the account is available - this will return the account you need.
  3. This time notice that the Account constructor logs in a different thread. You have written a multi-threaded application without using Thread or Callable or …​

20.2.5. Fetch multiple Accounts using a Web Client

  1. TODO-05: This time use the WebClient to fetch all the accounts from http://localhost:8080/accounts. The code is similar to before but this time use the bodyToFlux(Account.class) method.
  2. TODO-06: Subscribe to the result and pass a lambda to the subscribe() to count each item using counter.incrementAndGet().
  3. Run the test, it should pass by returning 21 accounts.

20.2.6. Web Client vs RestTemplate

  1. So which is better? As this is not a Reactive Programming class we have written the code for you that runs 200 account fetches, first using a RestTemplate and then using a WebClient.
  2. TODO-07: Examine the RestTemplate code. Very straightforward. Since there are only 21 accounts we use id % TOTAL_ACCOUNTS to generate the account id to fetch, as id iterates from 0 - 199.
  3. TODO-08: The WebClient code is more involved. We use the doOnSuccess() method which is just a callback that gets invoked for each item returned in the Flux. Here we increment a counter each time it is invoked. When we reach 200 we determine how long the whole test took to run.
  4. TODO-09: Run the test - the RestTemplate is quickest due to the overhead of multi-threading. The WebClient is not providing any advantage - yet.
  5. TODO-10: But what if the Server was slower. In the AccountsController find TODO-10 and uncomment the line to add a 50s delay ot each response.
  6. TODO-11: Run the test again and now the WebClient` is much faster due to its ability to run multiple requests in parallel.

Note: Since we only have a Spring MVC controller we are stuck with the delay we introduced, locking up threads from our thread pool. However if we converted it to a WebFlux reactive controller, we could mitigate the delay by returning a Mono (putting the delay in the Mono’s callback code). The delay is still there (as it would be if you had to perform a slow database lookup or talk to another system) but the Controller method can return immediately and free-up its thread.

Congratulations, you have finished the lab.