<

I just announced the new Spring 5 modules in REST With Spring:

>> CHECK OUT THE COURSE

1. Overview

In this tutorial, we’ll write a simple application showcasing a fully reactive flow using Spring Data Reactive MongoDB and Spring SSeEmitter.

On one side, we’ll apply Spring Data Reactive MongoDB to save data through a Mongo reactive database and combine it with the Server-Sent-Events mechanism to notify subscribed clients about incoming data.

Additionally, we’ll take advantage of Spring Boot’s Kotlin support.

So, let’s start!

2. Setup

First of all, we have to configure our Maven project adding the Spring Data Reactive MongoDB dependency in our pom.xml:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb-reactive</artifactId>
</dependency>

Moreover, to use Kotlin, we’ll need to add the Kotlin standard library to the same file:

<dependency>
    <groupId>org.jetbrains.kotlin</groupId>
    <artifactId>kotlin-stdlib</artifactId>
</dependency>

Now, we’re ready to start developing our application. We’ll start configuring the environment to support reactive programming and Mongo DB, so let’s go!

3. Reactive Mongo Configuration

The first thing we have to do is to configure our project to support reactive Spring Data. We’ll add a new class extending from AbstractReactiveMongoConfiguration to configure the Mongo reactive client and the Spring Data Repository:

@Configuration
@EnableReactiveMongoRepositories(
  basePackageClasses = arrayOf(EventRepository::class))
class MongoConfig : AbstractReactiveMongoConfiguration() {
 
    override fun getDatabaseName() = "mongoDatabase"
 
    override fun reactiveMongoClient() = mongoClient()
 
    @Bean
    fun mongoClient() = MongoClients.create()
 
    @Bean
    override fun reactiveMongoTemplate()
     = ReactiveMongoTemplate(mongoClient(), databaseName)
}

This configuration isn’t required if we want to interact with MongoDB in a non-reactive manner. Note that we have to add the @EnableReactiveMongoRepositories tag to let the configuration know where our Spring Data repositories are.  

Following this, we’re now ready to start implementing the main functionality. The first thing we’ll do is develop a new data class to persist the incoming information and then, a related Spring Data reactive repository to manage that persistence.

4. Document

The document is the unit of storing data in a MongoDB database. This unit uses JSON style for storing data.

In our project, we’ll keep it simple using a dummy document called Event with two attributes: id and name:

@Document
class Event(id: String, name: String)

5. Spring Data Reactive Repository 

The goal of Spring Data abstraction is to reduce the amount of code required to implement data access layers for persistence stores.

Consequently, the reactive version works the same so, we’ll have the following line to implement a whole reactive repository:

interface EventRepository : ReactiveMongoRepository<Event, String>

6. Controller 

The Controller class will be responsible for sending a Server-Sent Event whenever any reactive data is saved.

The method saveAndSend will first save the incoming data into our Mongo Reactive database delegating this action to our EventRepository. 

Consequently, we’ll add a new endpoint that creates and saves new Events.

First, let’s see the Kotlin code:

@GetMapping(value = "/save", 
  produces = arrayOf(MediaType.TEXT_EVENT_STREAM_VALUE))
fun saveAndSend(@RequestParam("eventName") eventName: String) =
  eventRepository
    .save(Event(UUID.randomUUID().toString(), eventName))
    .flux()

As we can see, after saving the new data, the Spring Data reactive repository will return an SSE that will be sent to the subscribed client.

At this point, we can say that we have a complete reactive Kotlin server-side project. We already have all the needed elements to execute our Spring Boot application.

Thus, we’ll now take a look at how to create a simple web client to send and receive all our created Server-Sent events.

7. Subscriber

Here we have a simple web client that will be able to save data and receive changes from the server.

Let’s see how it’s implemented:

7.1. Send Data

The client will save the typed event name through the Save new event button.

This, in turn, will make an HTTP request to our server endpoint saveEvent:

<form method="get" action="/save">
    <input type="text" name="eventName">
    <button type="submit">Save new event</button>
</form>

7.2. Receive Data

On the other hand, the client will be listening to save endpoint as well. Note that each programming language has his specific frameworks to manage SSE.

However, for our example, we’ll keep it as simple as possible:

<div id="content"></div>
<script>
    var source = new EventSource("save");
    source.addEventListener('message', function (e) {
        console.log('New message is received');
        const index = JSON.parse(e.data);
        const content = `New event added: ${index.name}<br>`;
        document.getElementById("content").innerHTML += content;
    }, false);
</script>

8. Conclusion

In conclusion, Spring Data MongoDB has been updated to leverage the reactive programming model introduced in Spring Framework 5. We now have a simple way to use this programming paradigm and Server-Sent events.

Hence, this supposes an alternative to the traditional non-reactive applications the problems with the database blocking.

The implementation of this example can be checked in the GitHub project.

This is a Maven-based project, so then, execute the Spring Boot application to see how it works. Don’t forget to run the Mongo DB server first.

I just announced the new Spring 5 modules in REST With Spring:

>> CHECK OUT THE LESSONS

  Subscribe  
newest oldest most voted
Notify of
Vinicius
Guest
Vinicius

Hi, Eugen!
First of all, thanks for this tutorial, it`s Great.

The link to git repo seems to be wrong, can u take a look ?

Grzegorz Piwowarek
Editor

Good catch – fixed in the article. It’s this one: https://github.com/eugenp/tutorials/tree/master/spring-data-5-reactive

Marco
Guest

Hi Eugen,
Thank you for sharing this article, I really enjoyed reading it.

Just a question: have you ever tried to compare your solution with a classical non-reactive in order to understand the scalability of the last? Well, I’m struggling to make solution based on reactive Mongo repository performant as non-reactive solution but I keep on seeing better result with non-blocking.

I published my result on github https://github.com/MarcoGhise/ReactiveMongoPerformance and I’d really appreciate to know what you think about this issue.

Thank you!

Grzegorz Piwowarek
Editor

Remove the “.subscribeOn(Schedulers.elastic())” from https://github.com/MarcoGhise/ReactiveMongoPerformance/blob/master/account-reactive/src/main/java/com/account/rest/AccountController.java#L27

and from all endpoints – Spring “subscribes” to a given Flux/Mono by itself – this might be creating some unwanted side-effects.

Let me know if that helped 🙂

Marco
Guest

Hi Grzegorz, Thanks for the reply.

Trying removing the subscriber directive (from reactiveAccountRepository.findAll().subscribeOn(Schedulers.elastic()); to reactiveAccountRepository.findAll()), I still got similar result.

I adopted a dedicate test environment and I got average response time faster in non-reactive solution (2326ms vs 3570ms).

Have you got any other ideas? I’m really wonder how this issue hasn’t been reported in any Jiira. My solution is so basic that I’m figuring out I’m facing a real library limits.

Grzegorz Piwowarek
Editor

I’m 99% sure something is wrong with your set-up – let me have a closer look.

Grzegorz Piwowarek
Editor

“Also, reactive solution opens a huge number of connections to MongoDb.” – this part is especially alarming. When using a non-blocking solution, the number of connections should stay pretty low and be stable

Marco
Guest

Thanks Grzegorz for your time!
I agree with you at 100% but I can’t work out the solution; I run reactive solution with a 4core processor and I checked through VisualVm the threads.
I’ll keep on trying different configurations to understand what’s wrong.

Grzegorz Piwowarek
Editor

On your place, I’d start by isolating the problem. For now, you are testing the performance of the whole application and not just the Reactive Mongo. So, let’s replace the reactive repository in the reactive project with the standard one, and let’s see how this compares to the reactive version

Marco
Guest

Thanks Grzegorz for your suggestion; I splitted the problem checking the application server (Netty and Tomcat) then the method’s response type (from Flux to List) and finally the repository type.
I can say my problem comes definitely by the repository type. ReactiveCrudRepository opens one connection per user. This is unbearable for systems scalability.
Probably this is a limit of the library, thank you for your time!

Grzegorz Piwowarek
Editor

Yes, and that definitely should not be the case. I suggest reaching out to the maintainers – you might be missing some simple config entry