This guide demonstrates the capabilities of Spring WebFlux to build a Reactive Web Application, supported by an application that you can build step-by-step. To avoid too simple scenarios in which we use also a Spring backend as a client, you will complete the full reactive stack with a client application in Angular 4. You will also use Server-Sent Events to communicate the backend with the frontend.
About this guide
I decided to write this guide to show others what I learned about Reactive Web using Spring WebFlux. Bear in mind that I’m not a Reactive Web guru, so use this tutorial as a reference and never as the only truth. When developing your own code you should read the reference documentation, compare this with other articles, and implement the solution that better fits your needs.
- Introduction. This post, which describes the goals of the guide, an overview of the main concepts covered and the holistic view of the application you'll build.
- The backend side. Covers the creation of the Spring Boot application and the development of the backend services. It focuses especially on the Controllers using WebFlux.
- The frontend side. Goes through the development of an Angular application that consumes the reactive endpoint created in the previous section.
- Conclusions. This part completes the guide, summarizing what you've built and comparing the Reactive approach with the classic approach. It gives some advance insight on performance, testability, etc.
The objective of the Guide is learning Reactive Web development by building an application from scratch (or navigating through the code if you prefer that). The chosen technologies are Spring WebFlux, Spring Data JPA Reactive Repositories, Spring Boot 2, Angular, RxJS and EventSource API.
Besides, the guide covers the classical Spring MVC Web alternative and compares MVC with the Reactive Web approach.
Full-Reactive Web Overview
Reactive Patterns applied to the Web
Non-blocking functionality and Reactive patterns are extended nowadays but, when we talk about backend Controller and Web layers, most of the APIs are based on blocking and imperative programming.
To try to clarify a bit better how this new Reactive Web approach differs from previous ones, let’s compare it with two existing techniques:
- Servlet 3.0 specification introduced asynchronous support so, from Java, you can optimize the usage of container threads using
DeferredResultas result of our controllers. But, in any case, from the client's point of view, the call is still blocked on the server side. Besides, the imperative way of using the API is far from being developer-friendly so they are seldom used.
- From the client's point of view, you can rely on asynchronous patterns as well. This is the most popular way of performing a request to the server, using a promise or in general any way of running the request and wait for the response in a different thread so you don't block the main flow of your application but leave the task in the background instead. But, again, the additional thread is blocked waiting for a response that might take a long time to complete.
The main advantages of the new Reactive Web patterns are not only that you can apply streams and the publisher/subscriber patterns to your developments, but also that you can combine it easily with capabilities like Server-Sent Events to build a non-blocking, full reactive stack in which your Web clients can get parts of the response as soon as they become available.
WebFlux and Project Reactor
Spring 5 offers us a non-blocking, reactive alternative to the standard Spring MVC Web approach: Spring WebFlux. It is based on the Project Reactor (also part of the Spring family), which itself follows the Reactive Streams Specification so the standard concepts of Reactive Programming are available there: Publisher, Subscriber, Observable, etc.
We’ll cover WebFlux and Project Reactor in more detail in the next post of the series.
Blocking vs. Non-Blocking: Advantages
So WebFlux implements a non-blocking web API. That sounds pretty cool but, what does that mean exactly? Why should we care about non-blocking web calls?
Well, a first good argument is supported by the fact that everybody has experienced slow Internet connectivity: it’s annoying, but it’s even worse with a blocking call and a classic backend controller and client interface. Let’s say you’re searching for cheap mobile phones on our favorite online store. You enter the query terms and press ‘Search’. For some reason (you might be traveling in the subway), your Internet connection is slow at the moment so you need to wait for 15 seconds and then… the full first page with the results, all at once.
Something similar to the slow connection issue would happen if the server is busy. Let’s imagine that many users are searching for products on that online store at the same time. In that case, the database is congested and queries take long to get to 50 results. Now the blocking part of the interaction is not the web interface but the database connection; however, the result is the same. You, the user, need to wait until the query is fully completed to get your list of products.
Sending pieces instead of the whole block
A better outcome for both scenarios would be that the user gets, for example, three items on the screen per second, instead of all at once after a while. That’s a first non-blocking advantage: faster availability of the data, which in our example means a better user experience. We as developers should aim for that and move away from blocking calls. According to a study from Google, a 53% of mobile users abandon a site if it takes more than 3 seconds to load.
In a non-blocking scenario, the database retrieves results as soon as they’re available. Same for the Web Interface (let’s say HTTP). Business logic should be prepared to process items, e.g. by using data streams. Finally, to close the loop and benefit from a full non-blocking stack, the client side should be able to process the data coming in a non-blocking way.
The second big advantage is related to thread usage: in a non-blocking system, you don’t have threads waiting to be completed. Instead, you complete your thread quickly since you don’t wait for the response - you’ll be notified. This advantage is important if you have a system that may perform slowly sometimes, thus accumulating a lot of blocked threads and eventually not being able to process more.
Is Non-Blocking the same as Reactive?
It depends on who you ask that question. But, since ‘Reactive’ is a generic concept describing that you’re acting triggered by a previous action, you could say that programming following a non-blocking approach is programming in a reactive way. In that broad scope of the word ‘reactive’, using a
CompletableFuture, or a promise, would be reactive programming. Note that, in this guide, I’ve preferred to call them asynchronous techniques.
Yet it’s more common to refer to reactive programming as applying one of the existing APIs that follow the Reactive Streams spec. Some popular reactive frameworks are ReactiveX (RxJS, RxJava, etc.), Akka, and Project Reactor (on top of which WebFlux is supported). Java 9 also implements the reactive streams specification with the Flow API. All these frameworks implement (with some slight difference in naming) patterns like Observable, Publisher and Subscriber.
Additionally, some reactive frameworks bind the generic reactive idea described above with the concept of backpressure. It means that the subscriber (consumer of the data) has total control of the stream, so the data flows only when the consumer reads it. Project Reactor implements backpressure, as we’ll see and demonstrate later in this series.
To showcase the reactive capabilities you’ll create a client web application that receives quotes from the book Don Quixote. Instead of just asking for the quotes using a standard blocking call, you’ll open a Server-Sent Events channel. The backend application will send these events once there is a subscriber (our client application). This part is supported by WebFlux returning a
Flux<Quote> object (don’t worry, I’ll explain that). On the Angular end, you’ll model the connection using an
EventSource object, mapped to a RxJS’s
The part that subscribes to the Observable is the Angular’s
AsyncPipe, which supports reactive streams.
At the same time that you build the Full Reactive Stack, you’ll create a classic blocking approach too. We’ll use it to compare both approaches from the implementation and runtime perspectives.
We’ll dive into details in the rest of the articles of this guide.
Continue reading the Guide
Now continue to the next post in this series, to see how to implement the backend side of our application with Spring WebFlux.