Project Reactor

Language: Java

Reactive Programming

Project Reactor was developed by Pivotal (now part of VMware) to provide a fully reactive foundation for Java applications, particularly in Spring ecosystems. It is widely used in reactive web applications, microservices, and data streaming pipelines to improve scalability, responsiveness, and resource utilization.

Project Reactor is a reactive library for building non-blocking, asynchronous, and event-driven applications on the JVM. It provides types like Mono and Flux to handle zero-to-one and zero-to-many reactive sequences respectively, following the Reactive Streams specification.

Installation

maven: Add dependency in pom.xml: <dependency> <groupId>io.projectreactor</groupId> <artifactId>reactor-core</artifactId> <version>3.5.9</version> </dependency>
gradle: Add dependency in build.gradle: implementation 'io.projectreactor:reactor-core:3.5.9'

Usage

Project Reactor provides reactive types like Flux and Mono, operators for transformations, filtering, combining, error handling, and scheduling. It integrates seamlessly with Spring WebFlux and reactive databases for end-to-end non-blocking applications.

Creating a Mono

import reactor.core.publisher.Mono;

Mono<String> mono = Mono.just("Hello Reactor");
mono.subscribe(System.out::println);

Creates a Mono that emits a single value and prints it.

Creating a Flux

import reactor.core.publisher.Flux;

Flux<Integer> flux = Flux.just(1, 2, 3, 4);
flux.subscribe(System.out::println);

Creates a Flux that emits multiple values and prints each one.

Transforming data with map

Flux<Integer> numbers = Flux.just(1, 2, 3, 4);
numbers.map(x -> x * x).subscribe(System.out::println);

Applies a transformation to each emitted value using the map operator.

Filtering data

Flux<Integer> numbers = Flux.range(1, 10);
numbers.filter(x -> x % 2 == 0).subscribe(System.out::println);

Filters even numbers from a sequence using the filter operator.

Combining sequences

Flux<String> flux1 = Flux.just("A", "B");
Flux<String> flux2 = Flux.just("1", "2");
Flux.zip(flux1, flux2, (a,b) -> a + b).subscribe(System.out::println);

Combines two sequences element-wise using zip.

Error handling

Flux<Integer> numbers = Flux.just(1, 0, 2);
numbers.map(x -> 10 / x).onErrorReturn(-1).subscribe(System.out::println);

Handles division by zero errors and provides a fallback value.

Scheduling and threading

Flux.range(1, 5)
    .publishOn(Schedulers.parallel())
    .map(x -> x * 2)
    .subscribe(System.out::println);

Executes the Flux operations on a parallel scheduler for non-blocking concurrency.

Error Handling

OnErrorNotImplementedException: Occurs when a reactive stream emits an error but no error handler is defined. Always provide onError handlers.
IllegalStateException: Occurs when subscribing multiple times to a single-use Mono or Flux improperly. Ensure proper usage.
NullPointerException: Reactor does not allow null values to be emitted. Use Mono.empty() or Flux.empty() instead of null.

Best Practices

Use Mono for single-value sequences and Flux for multiple-value sequences.

Avoid blocking calls inside reactive pipelines to maintain non-blocking behavior.

Handle errors gracefully with onErrorReturn, onErrorResume, or retry operators.

Use proper Schedulers for asynchronous execution and resource management.

Integrate with Spring WebFlux or reactive database clients for end-to-end reactive applications.