Integrating Web Services in Java: Performance & Robustness

5
minutes
Mis à jour le
12/1/2024

Share this post

Explore WebFlux library for asynchronous API calls setting up defensive design and error handling. Mastering these concepts empowers developers to build robust, high-performance applications.

#
Java
#
API
#
Testing
Timothé Berthier
Software Engineer

In the ever-evolving landscape of web development, the need for efficient and responsive applications has become paramount. Reactive programming is a powerful paradigm that prioritizes responsiveness through asynchronous data streams, excelling in scenarios with concurrent events. Its non-blocking model enhances performance, making it ideal for real-time applications. In summary, reactive programming offers an efficient solution for managing concurrency and ensuring responsiveness in modern applications.

A powerful solution to meet these demands is the Spring Webflux library which offers a compelling solution. In this article, we will delve into the WebFlux framework, explore the nuances of Mono, and understand how to harness their capabilities.

WebFlux Presentation

Spring WebFlux is a reactive programming framework designed to handle the demands of modern, event-driven, and non-blocking architectures. Its main advantage lies in its ability to handle a large number of concurrent connections with lower resource consumption compared to traditional synchronous frameworks.

Why WebFlux

The primary use case for WebFlux is scenarios where high concurrency and responsiveness are crucial, such as real-time applications, streaming services, and applications with varying loads. WebFlux is non-blocking, allowing for better resource utilization and responsiveness under heavy loads.

Flux and Mono classes

WebFlux streams are asynchronous sequences of data emitted over time. In this reactive model, represented by the Mono and Flux classes, streams enable efficient non-blocking operations on either a single element (Mono) or multiple elements (Flux). These streams are utilized to handle concurrent connections and asynchronous operations, providing a high-performance solution for applications requiring responsiveness, such as streaming services and real-time applications.

Mono vs. Flux

Mono is suitable for scenarios where a single result is expected, making it ideal for tasks such as fetching a specific user's data. On the other hand, when dealing with scenarios where multiple items need to be processed asynchronously, such as handling a stream of events or messages, the Flux class is more appropriate.

How to Use It

Let's now explore how to use Mono and code a client for making synchronous and asynchronous calls.

We will here use the ExchangeRate-Api, which provides the different exchange rates for a dollar. The API does not require any authentication.

Basic Sync Call with Mono

In this example, we create a Mono by casting the response into the specified class, String.class here, and the block() method is used to obtain the result synchronously.

Making It Asynchronous

Here, we leverage the subscribe method to perform the operation asynchronously. The callbacks handle the value emitted, errors, and the completion of the operation. The thread can continue running while awaiting for the API response.

Defensive Design

While working with reactive programming, it's crucial to implement defensive design to handle potential errors gracefully. Let's explore various error scenarios and their management strategies.

No response (Timeout)

In this example, a timeout of 5 seconds is set. After that, an error will be logged, depending on the exception raised.

Response is invalid

If the response does not match the requirements (missing required data for exemple), the flatMap statement will return a Mono on error.

API responds with an error

Note that some Web Services have a public list of retryable errors. That means you should implement a retry strategy for those errors, and those errors only. If your partner does not have that list, you may for instance want to implement a retry strategy for all 5xx errors.

Here, on a 5xx HTTP response, the app will retry 3 times, with a 1 second delay between each.

To prevent retry-storms, you can also configure a jitter. A jitter adds randomness to your retry strategy, and prevents all clients from retrying failed calls at the exact same time, and with the same period of time.

Then, specifying how to handle specific HTTP errors:

Let’s see what happens here:

HTTP errors handing example

Testing

We won’t dive into the details of testing a client in this article. A library I use to stub and mock web services is WireMock. You will find out all necessary materials to configure it properly in the following courses:

https://www.baeldung.com/introduction-to-wiremock

https://www.baeldung.com/wiremock-scenarios

Then, you will need an additional library to make assertions on asynchronous calls: awaitility and StepVerifier are great options.

Knowing that web clients are a recurrent source of bugs in web apps, I suggest you try developping yours using the Test-Driven Development methodology.

In conclusion, mastering reactive programming with WebFlux and Mono empowers developers to build robust and responsive applications. By understanding the core concepts and adopting defensive design strategies, you will harness the full potential of these technologies.

https://www.baeldung.com/spring-webflux

https://www.baeldung.com/spring-webflux-retry

https://docs.spring.io/spring-framework/reference/web/webflux.html