Overview:
In this tutorial, I would like to demo Spring WebFlux Streaming Response (aka Server Sent Events) – a mechanism for pushing notifications/messages to the browsers from the back-end application in real time.
Spring WebFlux Streaming:
Traditionally browsers have to make a request to the server to fetch the latest information. It has worked just fine so far. When we have to do periodic polling like this, most of the times there might not be any updates! When we have multiple clients bombarding the server every second for updates when there are not much updates, it is simply wasting resources and makes unnecessary network calls.
Server Sent Events is a standard for transmitting data to the client applications using the persistent connection established between the client and the server. With Server Sent Events (SSE / Event Stream) approach, our server notifies the browser when the server has some updates in more efficient way.
This can be easily achieved by using Spring WebFlux Streaming response.
Sample Application:
Let’s consider a simple application for people having depression. Our application continuously publishes Jokes every 3 seconds & keep the clients happy.
- Client/browser accesses the index.html or the home page of the application
- Home page could be containing information like stock prices, some latest news updates etc
- In our case, the home page will contain some joke.
- Like stock prices update, Jokes will be generated periodically in the server side which needs to be shared with clients!
Jokes API:
We will be using below URL to get random jokes. It is a simple GET request without any authentication.
https://joke.deno.dev/
The response payload is as shown below. Jokes are in the Q & A format.
{
"id":120,
"type":"general",
"setup":"How do hens stay fit?",
"punchline":"They always egg-cercise!"
}
Project Setup:
Create a simple Spring Boot application with these dependencies.
Joke – DTO:
Let’s start with creating a simple DTO class for the Joke object.
@Data
public class Joke implements Serializable {
private String setup;
private String punchline;
}
Application Configuration:
In our configuration class we create couple of beans.
- We need the Spring’s Reactive WebClient to make periodic requests to the public Joke API.
- We need a Reactor Sink object which will act like both subscriber and publisher. That is, when we receive a new joke from the WebClient, we need to pass it to the subscriber.
- This Reactor Sink would be also acting like a Publisher for the browsers and pass the jokes to the browsers via Event Stream. For that we create Flux bean from Sink.
@Configuration
public class AppConfiguration {
private static final String JOKE_API_ENDPOINT = "https://joke.deno.dev/";
@Bean
public WebClient webClient(){
return WebClient.builder()
.baseUrl(JOKE_API_ENDPOINT)
.build();
}
@Bean
public Sinks.Many<Joke> sink(){
return Sinks.many().replay().latest();
}
@Bean
public Flux<Joke> flux(Sinks.Many<Joke> sink){
return sink.asFlux().cache();
}
}
Service:
Here we create a Spring component / service. We autowire the webclient and sink instances. We make the call to the public API periodically to get random jokes and pass it to the sink instance. That is it.
@Service
public class JokePublisher {
@Autowired
private WebClient webClient;
@Autowired
private Sinks.Many<Joke> sink;
@Scheduled(fixedRate = 3000)
public void publish(){
this.webClient
.get()
.retrieve()
.bodyToMono(Joke.class)
.subscribe(this.sink::tryEmitNext);
}
}
Spring WebFlux Streaming – Server Side:
Our rest controller has a single endpoint. We autowire the Flux instance here through which our clients/browsers subscribe to the Jokes. Whenever the client makes a request to the /joke endpoint, the client is subscribing to the Flux which keeps on publishing new jokes whenever it receives new one!
- Make a note of the TEXT_EVENT_STREAM_VALUE int he GetMapping which uses the mime type text/event-stream to indicate the browser that the server would keep pushing updates (stream of updates).
@RestController
public class StreamController {
@Autowired
private Flux<Joke> flux;
@GetMapping(value = "joke", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Joke> getJoke(){
return flux;
}
}
Spring WebFlux Streaming – Client Side:
Our server side application is ready! We can test this directly by calling the joke endpoint. But Lets create a simple index.html with bootstrap theme to test the complete end-to-end behavior.
The index.html contains a div like this.
<!-- Page Content -->
<div class="container">
<div class="row">
<div class="col-lg-12 text-center">
<h1 class="mt-5">Joke of this moment!</h1>
<p class="lead">This site will keep you laughing every 3 seconds using Server sent events!</p>
<div class="card text-left mt-5" style="width: 80rem">
<div class="card-header" id="qn">
Joke Qn
</div>
<ul class="list-group list-group-flush">
<li class="list-group-item" id="ans">Joke Ans</li>
</ul>
</div>
</div>
</div>
</div>
The browser would keep getting new jokes every 3 seconds via Event stream. The browser should subscribe to our flux. The browser does not have to be refreshed.
The client side javascript is very simple as shown here. We just give the endpoint to subscribe to. The event.data contains the DTO information from which we get the specific information (like setup and punchline) and update our HTML.
var source = new EventSource("joke");
source.onmessage = function(event) {
let data = JSON.parse(event.data);
document.getElementById("qn").innerHTML = data.setup;
document.getElementById("ans").innerHTML = data.punchline;
};
Spring WebFlux Streaming – Demo:
That’s it! Let’s run the application and look at the application behavior.
Summary:
We were able to successfully demonstrate Spring WebFlux Streaming response (Server Sent Events / SSE / Event Stream). You could ask how to send specific messages to specific clients when there are multiple subscribers. It can be done very easily as well! Check out below article which I had done with MongoDB.
Learn more on Spring WebFlux.
The source code is available here.
Happy learning 🙂
Hi, Evan from your udemy class.
After I finish all your lectures “Reactive microservices with WebFlux”, I decided to start my new project based on WebFlux.
While I was struggling to port my messaging adaptor to Webflux, I came up with your lecture about SSE. My idea is that the kafka consumer emits the messages (like what Service does here in your blog) to the Sink and the consuming class consumes it by autowiring Flux (like what Controller does here in your blog).
But I have no knowledge on how Sink and Flux-autowiring work. Could you recommend where I can catch up the concepts of them? (I appreciate if you recommend your another lecture or documents)
I see. If you had enrolled in the webflux course, in the very first section I would have mentioned that the course is Part-2 of the reactive course series. That is I strongly assume that students are aware of Sink/flux/backpressure/various schedulers etc. If you need to learn them , you need to enroll PART-1 of the series which is Reactive Programming. You can learn how these things are connected.