Server Sent Event

 




This post describes basics of

  • SSE concepts
  • SSE use cases
  • How does SSE work
  • Message Formats
  • SSE code on Client side and Server side
  • SseEmitter connection keep alive time
  • Auto Re-connect mechanism

SSE Concepts


Server Sent Events are the events ( data ) sent from server to the client over HTTP connection.

This connection is one directional connection from server to client. Meaning that, once the client connects to server, then there after server will send any real-time notifications generated on the server side, to the client.Client can be mobile app or browser based app, or any client that support HTTP Connection.

SSE use cases

You would have seen SSE use cases around you in day to day life

  1. Continuous update about train time notifications on the display panel on the platform.
  2. Continuous Rolling of Stock updates.
  3. Real time counter increment of your social media ‘likes’ icon and could be more…

How does SSE work

Client initiates a connection to server over http, this can be done by client calling a rest end point on the server, in return, the response should have content-type header values as text/event-stream

This tells the client, that a connection is established and stream is opened for sending events from the server to the client.

In the browser you have a special object called, EventSource, that handles the connection and converts the responses into events.

Message-Formats

SSE only supports text data. Meaning, server can only send text data to the client.
Binary streaming, while possible, is inefficient with SSE. In that case, WebSocket would be good choice for binary data transfer.

SSE code - Client side and Server side

Client Side Code

EventSource object is the core object supported by browser.
To open a connection to the server, client will need to instantiate EventSource object.

1
const eventSource = new EventSource('http://localhost:8080/subscribe/'); 

Browser sends this GET request with accept header text/event-stream.The response to this request, must contain header content-type with value text/event-stream and response must be encoded with UTF-8.

To process these events in the browser an application needs to register a istener for the message event.

The property data of the event object contains the message

1
2
3
4
eventSource.onmessage = event => {
  const msg = JSON.parse(event.data);
  // access your attributes from the msg.
};

Client api supports certain events like open and error. Open event occurs as soon as 200 response is received by client for /subscribe GET call.
Error event is received by client, when there is any network error or server terminates the connection.

Server Side Code

Http Response to the above GET request on /subscribe end point must contain the Content-Type header with the value text/event-stream.

Spring Boot supports SSE by providing SseEmitter object. It was introduced in spring version 4.2 ( spring boot 1.3 ).

Create a spring boot application from start.spring.io and select web as dependency.
You can have a controller with rest end point GET with /subscribe allows client to establish connection.
Another rest end point POST with /event allows us to submit new events on the server.
This POST with /events or similar end point, can be called from any other server side component to send real time notification.

This /event end point, will then send event to connected clients.

Each client connection is represented with it’s own instance of SseEmitter.

One limitation with spring SSE is , it does not give you tools to manage
these SseEmitter instances. So, for this example, I have used a list that stores SseEmitter objects and release objects on errors, completion or timeout scenarios.

SseEmitter object is created as below

1
SseEmitter emitter = new SseEmitter();

SseEmitter connection keep alive time

By default, Spring Boot with the embedded Tomcat server keeps the SSE HTTP connection open for 30 seconds.We can override this 30 seconds via configurations.

spring.mvc.async.request-timeout=50000

this entry will keep the HTTP connection open for 50 seconds. Alternatively, you can directly use SseEmitter constructor to pass this timeout value as below

SseEmitter emitter = new SseEmitter(150_000L); 
//keep connection open for 150 seconds

Auto Re-connect mechanism

The nice thing about Server-Sent Events is that they have a built in re-connection feature. Meaning that, if the connection is dropped due to server error then client will automatically tries to re-connect after 3 seconds.

The browser tries to send reconnect requests forever until he gets a 200 HTTP response back.

It’s a browser feature to wait for 3 seconds and then automatically reconnect.
This 3 seconds of time can be changed by the server by sending a new time value in the retry header attribute together with the message.

A client can be told to stop reconnecting using the HTTP 204 No Content response code.

Code Example =: 

back-end : server side

package com.example.demo;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
import java.awt.*;
import java.io.IOException;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

/**
* @author Suravi sasadara
* @date 2022-03-19 12.41
*/

@RestController
public class NewsController {

public List<SseEmitter> emitters = new CopyOnWriteArrayList<>();
//method for client subscription
@CrossOrigin
@RequestMapping(value = "/subscribe", consumes = MediaType.ALL_VALUE)
public SseEmitter subscribe(){
SseEmitter sseEmitter = new SseEmitter(Long.MAX_VALUE);
try{
sseEmitter.send(SseEmitter.event().name("INIT"));
}catch(IOException e){
e.printStackTrace();
}
sseEmitter.onCompletion(() -> emitters.remove(sseEmitter));
emitters.add(sseEmitter);
return sseEmitter;
}

// method for dispatching events to all client
@PostMapping(value = "/dispatchEvent")
public void dispatchEventToClients(@RequestParam String freshNews){
for( SseEmitter emitter : emitters){
try{
emitter.send(SseEmitter.event().name("latestNews").data(freshNews));
}catch(IOException e){
emitters.remove(emitter);
}
}
}

}

Client side
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<!-- CSS only -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<!-- JavaScript Bundle with Popper -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js" integrity="sha384-ka7Sk0Gln4gmtz2MlQnikT1wXgYsOg+OMhuP+IlRH9sENBO0LRn5q+8nbTov4+1p" crossorigin="anonymous"></script>
<title>SSE with JPS</title>
</head>
<body>
<div class = "row">
Hi JPS this is you server Sent Event project front end.
</div>
</body>
<script>
$(document).ready(function () {
var urlEndPoint = "http://localhost:8080/subscribe";
var eventSource = new EventSource(urlEndPoint);

eventSource.addEventListener("latestNews",function (event) {
console.log(event.data);
})
})
</script>
</html>

Demo
CURL => curl --location --request POST 'http://localhost:8080/dispatchEvent?freshNews=hi%20suravi%20sasadara'






For more explanation of above example => https://www.youtube.com/watch?v=T_JZzdPCkOU
                                          https://www.youtube.com/watch?v=HoxPgU4lFGE


Web-Socket vs Server Sent Events





Comments

Popular posts from this blog

Hibernate (Java) -- by jps sasadara

Observer Design Pattern & RxJava & @Async

JAVA uml Based cording <<< by jps sasadara >>>