01 November 2020

So far…​

In the previous post we introduced Reactor testing and backpressure.

To summarize all three parts:

  1. Part 1: Getting started with Reactor: Flux, Mono, zipping, Tuples, and retry

  2. Part 2: Backpressure and testing with Reactor

  3. Part 3: Spring WebFlux, Netty, and WebClient with a dip into JHipster

Prerequisites

For this post you should have a good understanding of Java and Spring Boot and have read the previous posts about Reactor.

JHipster

JHipster is a code generation tool primarily for building Spring-Boot based projects with either a React or Angular front end - although it can be extended via blueprints and there are several (one allows you to create a Vue.js front-end and one a Micronaut back-end for example).

It also has a feature allowing you to create a Spring Boot WebFlux project.

WebFlux is Spring’s framework for asynchronous, non-blocking web applications - and it works with Reactive types, especially project Reactor (Flux, Mono, etc.).

To get started, install Node (>10.20.1) and JHipster (latest)

  • Then, from the terminal, make a new directory (named "hip-webflux"), and run jhipster

  • Follow the prompts, and choose monolith and then Reactive (this will create a Spring Boot WebFlux project)

  • For database, you can pick MongoDB, or Cassandra (the reactive generator doesn’t support SQL at the moment)

  • For front end, you can pick React or Angular

  • Pick Maven or Gradle

…​This takes a while since it has to load a bunch of dependencies…​

Then you should see output like the following:

Server application generated successfully.

Run your Spring Boot application:
./mvnw

Client application generated successfully.

Start your Webpack development server with:
 npm start


> hip@0.0.1-SNAPSHOT cleanup /home/adavis/github/adamldavis/hip-webflux
> rimraf target/classes/static/

INFO! Congratulations, JHipster execution is complete!

JDL

After creating the basic project, let’s add some entities.

One of the cool things about JHipster is JDL (Jhipster Domain Language). Its a DSL for creating projects and entities. Create a file named "hip.jdl" and put in the following:

entity Person {
  name String required
  age Integer min(0) max(142)
}

(Feel free to mess around with your JDL and make it more complicated, but for this example, let’s keep it simple)

Import the JDL by running jhipster import-jdl hip.jdl

Now open the project with your favorite IDE (like IntelliJ IDEA for example) and take a look at the PersonController.

JHipster automatically creates an AuditEvent database for auditing (create, update, delete record events). Open up the file named "AuditResource" under the "com.mycompany.myapp.web.rest" package (Under src/main/java/). It should have a "getAll" method that looks something like this:

@GetMapping
public Mono<ResponseEntity<Flux<AuditEvent>>> getAll(ServerHttpRequest request, Pageable pageable) {
    return auditEventService.count()
        .map(total -> new PageImpl<>(new ArrayList<>(), pageable, total))
        .map(page -> PaginationUtil.generatePaginationHttpHeaders(UriComponentsBuilder.fromHttpRequest(request), page))
        .map(headers -> ResponseEntity.ok().headers(headers).body(auditEventService.findAll(pageable)));
}

WebFlux allows you to return Reactor types like Mono and Flux from controller methods and implements a reactive, asynchronous web server using Netty by default.

A typical web server uses a large number of Threads and handles each request in one thread. The problem with this is Threads in Java are kind of heavyweight (this should be fixed by project Loom and virtual threads in Java 15+). This allows a single Thread to block without holding back the entire application; however, the number of concurrent requests you can handle is limited by the number of threads your server can support.

Netty instead uses an EventLoop per Thread for I/O. WebFlux enables and simplifies the Java API using Reactive Streams (of which Reactor is one implementation).

No Blocking!

Since the threads in WebFlux are meant to be non-blocking, you must be careful not to block them. For example, don’t call block() or Thread.sleep. There’s a project called BlockHound to help enforce this rule throughout your application. (This doesn’t mean no threads can block; only blocking should happen in dedicated threads)

To implement this, your application should be reactive "all the way down". So if you have a database, reading from it should also be reactive and non-blocking. Projects like r2dbc and Spring spring-data-r2dbc help, among others.

WebClient

WebFlux also includes WebClient which is a reactive replacement for RestTemplate. It makes asynchronous, non-blocking HTTP requests.

You can create an instance of WebClient using the builder pattern starting from the static WebClient.builder() method. For example:

import org.springframework.web.reactive.function.client.WebClient;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
// later on...
WebClient myWebClient = WebClient.builder()
 .baseUrl("http://localhost:8080/api-to-call/")
 .defaultCookie("cookieKey", "cookieValue")
 .defaultHeader(HttpHeaders.CONTENT_TYPE,
   MediaType.APPLICATION_JSON_VALUE)
 .build();

This builds a WebClient with given baseUrl. It also provides a cookie and header to use for every request. There are many more methods to configure the WebClient. Each request starts with defining the HTTP method, then you can specify an additional URL path (with or without path variables) and call exchange which returns a Mono<ClientResponse>. For example, to get a person from some /v1/persons REST api:

// get the Course with ID=1 and print it out:
myWebClient.get()
    .uri("/v1/persons/{id}", 1L)
    .exchange()
    .flatMap((ClientResponse response) ->
      response.bodyToMono(Person.class))
    .subscribe(person -> System.out.println("person = " + person));

The WebClient thus enables all of your HTTP calls to be reactive. The Spring team likes WebClient so much, they recommend everyone uses it over RestTemplate.

Schedulers

One important type in Reactor is Schedulers. Schedulers provides various Scheduler flavors usable by publishOn or subscribeOn for a Flux or Mono. For example:

mono.publishOn(Schedulers.elastic())

Makes a Mono suitable for running blocking tasks like HTTP calls.

Conclusion

I hope you got something from this three part series. There’s much much more to learn about Reactor, but I hope this is a good introduction.

Reactive programming in Java isn’t always necessary - it’s for enabling thousands of transactions a second, so you might not need it - but when any application becomes popular enough, you tend to need high throughput and performance.

Eventually when Java has virtual threads it will make threads more lightweight, however, Reactive will still be useful due to its backpressure handling, retry logic, and other benefits.

  1. Part 1: Getting started with Reactor, Flux, Mono, zipping, Tuples, and retry

  2. Part 2: Backpressure and testing with Reactor

  3. Part 3: Spring WebFlux, Netty, and WebClient (and JHipster)