An open API service indexing awesome lists of open source software.

https://github.com/perty/sse

Experiments with server side events and Spring Boot
https://github.com/perty/sse

Last synced: 12 months ago
JSON representation

Experiments with server side events and Spring Boot

Awesome Lists containing this project

README

          

# Experimenting with reactive and server sent events

Here is a couple of examples or rather, experiments around reactive programming.

I have used Spring Boot Webflux and R2DBC driver with a Postgresql database.

## Observations and learnings

### Server sent event

When setting up a listener for event streams, Chrome seems to be polling. That was not my expectation, I expected it to be a server push technology.

Is it a combination of the server implementation and the browser doing some kind of fall back?

The JavaScript for to register an ``EventSource``:
```
const evtSource = new EventSource("http://localhost:8080/events");
evtSource.onmessage = function (event) {
const newElement = document.createElement("li");
const eventList = document.getElementById("list");

newElement.innerHTML = "Event data : " + event.data;
eventList.appendChild(newElement);
}
```

The server side is using a reactive construction:
```
@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux> getMessages(@RequestHeader HttpHeaders headers) {
List lastIds = headers.get("Last-Event-ID");
String lastId = lastIds == null ? "0" : lastIds.get(0);
return messageService.getMessages(lastId);
}
```
By using the ``Last-Event-ID`` header attribute the last id the client can communicate which the last id it had received. Without it, all messages would be received again.

Wikipedia [https://en.wikipedia.org/wiki/Server-sent_events](https://en.wikipedia.org/wiki/Server-sent_events)

Mozilla [https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events)

### Reactive database

It is not just a replacement of the driver, there are several other things that are different, compared to using Spring JPA.

#### Entity annotation
The class to represents persisted data [MessageEntity](src/main/java/se/artcomputer/edu/sse/MessageEntity.java), do **not** have an `@Entity` annotation.

#### Generating primary id
The `@GeneratedValue` is not available, so I relied on Postgresql to generate a primary key.

#### Implement Persistable
I am not sure about this, but it seems to be important that the entity class ``implements Persistable``, which simply tells whether to insert or update.

#### Repository interface
Instead of using `JPARepository`, you need to use `ReactiveCrudRepository` which returns `Flux` objects. But the experience is similar in other aspects.

#### Flyway
Flyway does not support R2DBC (there is an issue about it) so I needed to add a section in the application configuration where Flyway gets a JDBC url:
```
flyway:
url: jdbc:postgresql://localhost:5432/sse
user: postgres
password: mysecret
```
It is enough, though, unlike other sources claim.

#### Creating many entities
When creating 1000 entities using a reactive construction, the order of the creation is not sequential, which I think is cool.

Here is the construction for creating entities and returning the data transfer object `Message`:
```
public Flux generate() {
return Flux.range(0, 1000).flatMap(this::create);
}

private Mono create(Integer n) {
MessageEntity entity = new MessageEntity();
entity.setMessage("This is generated " + n);
return messageRepository.save(entity).map(this::toDto);
}
```
As you can see, the messages are numbered and as said, the order of them are not they same in the database. This is evident from the primary key that is generated by Postgresql. Eg:

|id |message
|---|-------
|9 |This is generated 7
|10|This is generated 12
|11|This is generated 13
|12|This is generated 14
|13|This is generated 9
|14|This is generated 10
Cool, right?

#### Reading many entities

I would like to see the entities come trickling when querying for 1000 entities at once but they came all in one bunch. So I need to study this more.

Ok, so this put me on the right track somehow [Full Reactive Stack with Spring Boot, WebFlux and MongoDB](https://thepracticaldeveloper.com/full-reactive-stack-2-backend-webflux/).

The `delayElements` of `Flux` works in combination with server-sent events. It comes together here! By not stating the media type at the endpoint, I can use curl and accept header to get them trickling, like so:
```
curl -H "Accept: text/event-stream" http://localhost:8080/messages/slow
```
It keeps asking for more so when 1000 entities have been sent, it starts over. Apparently what a client does when using this protocol so it is not just the browser.

Ok, this start-over business has to do with `delayElements` somehow. If I remove it, curl will terminate after all elements. Not intuitive to me.

There might be some timing issues. Sometimes curl hangs, sometimes it terminates and sometimes it keeps asking for more.

With a `log()` inserted I can see what is going on.

Something, perhaps Netty, is doing `request(24)` when the server responding quickly. It ends with a `onComplete()`.

When slow, there are only `onNext(MessageEntity{id=919, message='This is generated 919'})` and they keep coming.