You love reactive streams. Great. The application you are working on is non-reactive and full of blocking calls. Not so great. In this article we will explore options to bridge reactive and non-reactive code and potentially make applications more performant.

Note: The examples feature Java and Spring Reactor, but the principles and pitfalls are similar for all reactive libraries. If you are using RxJS the article The Extensive Guide to Creating Streams in RxJS might be helpful. In case you don’t use Spring Reactor:

Flux is Observable in ReactiveX libraries

Mono is Single in some ReactiveX libraries. It’s an Observable that emits a only single value (or none). Kind of like a Promise or Future.

Schedulers

Before we start getting reactive let’s talk about schedulers. They are an important building block of reactive streams even though you may never have been in touch with them. A scheduler determines when and on which thread (Note: not in RxJS) operations are executed.

All reactive libraries allow you to change the default behavior using the publishOn or subscribeOn operators. I have written about this recently. I highly recommend a talk from Spring Reactor contributor Simon Baslé that features neat visualizations illustrating how these operators work (starting at 16:49).

The available types of schedulers differ from library to library. You can find a list for your preferred flavor in the scheduler section of the ReactiveX documentation or the docs of your library.

Usually you can choose between using shared schedulers or creating your own ones. The three most important ones to create in Spring Reactor are:

Single threads: Schedulers.single(...) creates one thread that will be shared by all operations

creates one thread that will be shared by all operations Limited parallelism: Schedulers.parallel(...) creates a fixed pool of threads that allows you to run operations in parallel

creates a fixed pool of threads that allows you to run operations in parallel Unlimited parallelism: Schedulers.newElastic(...) creates new threads as needed, reuses them and discards unused threads.

What‘s the point of creating your own scheduler? You gain full control over the threading behavior. If any stream in your application using a shared scheduler includes a blocking or expensive operation, then all other streams using the same scheduler are affected — they are blocked. If, however, a stream uses a custom scheduler (and all you need for that is one line of code) it’s unaffected.

Unblock calls

Take the following code:

class MyService {



public String blockingMethod() {

...

return this.blockingWebService();

} }

When we call blockingMethod the caller has to wait and the thread is blocked. A naive approach to introduce reactive streams would be to wrap blockingWebService into a stream like this:

public Mono<String> blockingMethod() {

...

return Mono.just(this.blockingWebService());

}

This is pointless. blockingWebService is called before we return the stream. The very least we can do is to defer the invocation of blockingWebService until subscription. The easiest way to achieve this is to use fromSupplier :

public Mono<String> blockingMethod() {

...

return Mono.fromSupplier(() -> this.blockingWebService());

}

Now blockingWebService is executed when someone subscribes to the returned Mono . And that also means that the caller can let it run on a different thread:

myService.blockingMethod()

.subscribeOn(Schedulers.single())

.subscribe();

The caller still needs to know that the method contains blocking code. Let‘s care about threading ourselves.

class MyService { private Scheduler scheduler = Schedulers.newElastic("myThreads");



public String blockingMethod() {

...

return Mono.fromSupplier(() -> this.blockingWebService())

.subscribeOn(this.scheduler);

} }

You may wonder why I have used an elastic scheduler. If I use a fixed pool or a single thread the method will block if enough callers invoke the method.

There is a catch: every subscribeOn later in the chain overrides our subscribeOn . That’s the caller’s choice though.

Everything ok now? Well, that depends on the number of subscribers. More one that in the next section.

Convert asychronous constructs

Maybe your code already uses asynchronous constructs like futures or promises. They usually can be easily converted to streams:

class MyService {



public CompletableFuture<String> nonBlockingMethod() {

...

return this.blockingWebService();

} } Mono.fromFuture(myService.nonBlockingMethod())

.subscribe(...);

If the code already runs in another thread we are fine. Or are we? Calling the web service does not block, but the service is called immediately, before anyone subscribes. Let’s fix that and also return stream:

public Mono<String> nonBlockingMethod() {

...

return Mono.defer(

() -> Mono.fromFuture(this.blockingWebService())

);

}

Perfect. Almost. Deferring the execution like we did has one side effect: The code is executed for every subscriber. You may or may not want that. If you opt for this solution then document the behavior so that clients can adapt their behavior if necessary.

Convert callbacks

If you use an API that works with callbacks, that‘s already asynchronous. But how can we make a stream of this? Most libraries use a create function / method, or the Observable constructor.

Here is a simple example without streams:

callWebService("http://foo", response -> {

//process the response

});

Now with streams:

Mono.create(sink -> {

callWebService("http://foo", response -> {

sink.success(response);

});

})

.map(...)

.subscribe(...);

All we have to do is to create a callback that only emits to the stream. Then you can process the result using the stream operators. Some libraries have dedicated functions for that. E.g. RxJS has bindCallback and fromEventPattern .

Run code in parallel

Run expensive code asynchronously

Imagine some code like this:

class MyService {



public String expensiveMethod() {

//expensive calculations

return result;

} } value = myService.expensiveMethod();

There is nothing wrong with that but the thread could be blocked for a long time. There is nothing wrong with that either. If, however, this blocks your application or a service and makes it unusable then you should make it run asynchronously.

One way to do that with reactive streams is to extract the calculation into another method and use the techniques present earlier:

class MyService { private Scheduler scheduler = Schedulers.newElastic("myThreads");



public Mono<String> expensiveMethod() {

return Mono.fromSupplier(() -> this.expensiveCalculations())

.subscribeOn(this.scheduler);

} private String expensiveCalculations() {

...

} } // Or public Mono<String> expensiveMethod() {

return Mono.create(sink -> {

//expensive calculations

sink.success(result);

})

.subscribeOn(this.scheduler);

}

Process data in parallel

One of the nice things Java 8 streams introduced was the ability to let data be processed by multiple threads in parallel simply by calling parallel() . We can do the same with reactive streams and we have more control over the threads. Note: Not all libraries support this.

Scheduler scheduler = Schedulers.newParallel("foo"); myStream

.parallel()

.runOn(scheduler)

.map(...)

.subscribe(...);

Please note that parallel() only prepares the stream for parallel processing. The actual threading behavior is defined with runOn . Without any arguments parallel divides the work into as many pieces as there are CPU cores. If we have four of them then four elements can be processed in parallel. Our scheduler provides as many threads as there are CPU cores as well. A perfect match.

Another scenario is to run several independent operations in parallel and do something when all have finished. Some libraries offer a forkJoin operator but the more generic solution is zip .

Flux.zip(stream1, stream2, stream3)

.subscribe(combinedResult -> ...); // Produces

// [ value stream1, value stream2, value stream3] |

In Spring Reactor the combined result is a Tuple . In other libraries it can be an array or list. If your streams emit more than one value the first ones, second ones etc. of all streams are combined.

There are some points to consider. The streams can only run in parallel if they run on different threads. Depending on the streams it may be necessary to introduce a scheduler for one, some or all streams:

Scheduler scheduler = Schedulers.newElastic("foo"); Flux.zip(

stream1.subscribeOn(scheduler),

stream2.subscribeOn(scheduler),

stream3.subscribeOn(scheduler)

)

.subscribe(combinedResult -> ...);

Another catch is that if one stream fails the combined stream will fail as well. If you need all operations to succeed in order to continue thats’s fine. Otherwise we can solve the problem with a fallback value or fallback stream:

Flux.zip(

stream1.onErrorReturn("),

stream2.onErrorReturn("),

stream3.onErrorReturn(")

)

.subscribe(combineResult -> ...);

Please note that Spring Reactor doesn’t allow null values. The subscriber must be able to deal with the fallback values. Another option is merge in conjunction with onErrorResume :

Flux.merge(

stream1.onErrorResume(e -> Flux.empty()),

stream2.onErrorResume(e -> Flux.empty()),

stream3.onErrorResume(e -> Flux.empty())

)

.buffer(3)

.subscribe(combineResult -> ...);

If an error happens we return an empty stream instead. You might wonder why I used buffer . Without it merge we would have a stream that emits 3 elements, one for each source stream (provided every source stream emits only one element). Buffer combines the elements into a list (array in some libraries) similar to zip . If one stream fails and we need to know which streams succeeded because we need to process the results, then we need to map the results of the source streams:

Flux.merge(

stream1.onErrorResume(e -> Flux.empty())

.map(v -> Tuples.of(1, v)),

stream2.onErrorResume(e -> Flux.empty())

.map(v -> Tuples.of(2, v)),

stream3.onErrorResume(e -> Flux.empty())

.map(v -> Tuples.of(3, v))

)

.buffer(3)

.subscribe(combineResult -> ...); // Produces

// [ [1, value stream1], [2, value stream2], [3, value stream3]] |

Conclusion

The best solution in reactive programming is to use a fully reactive stack. That’s not always possible. Using the techniques I presented you can still achieve a high degree of “reactiveness”. Schedulers and parallel processing can help you to improve performance with ease.

If you know some other useful techniques feel free to share them or links to existing sources in the comments.