Zero to Hero in Server Sent Events.

Karamveer Gahlot
5 min readJun 27, 2021

--

In this article we will learn
-> what is Server sent events.
-> Need for Server sent events.
->Alternatives to Server sent events.
->How to implement server sent events(Code Yay)
->what are the exceptions we face while implementing server sent events and how to avoid them.

Now before starting with server sent events we should be aware that there are two style of network communication.
-> Server Push — In this form of communication the request for a given transaction is initiated by the publisher or server. Ex- With Server push, it’s possible for a server to send new data to a web page at any time, by pushing messages to the web page.
->Server Pull — It is a style of network communication where the initial request for data originates from the client, and then is responded to by the server. Ex — web page has to send a request to the server to receive new data; that is, the page requests data from the server.

What is Server Sent Events?

Server-Sent Events (SSE) is a server push technology enabling a client to receive automatic updates from a server via an HTTP connection, and describes how servers can initiate data transmission towards clients once an initial client connection has been established.

Lets see how the communication happens between client and server in Server Sent Events.

To initiate a Server-Sent Events subscription, create a new EventSource object with a Server-Sent Events endpoints and begin listening for events. Once a connection has been established, and as long as the connection remains open, updates will be sent to the client each time a comment or reaction occurs.

var source = new EventSource(endpoint);

The EventSource interface is web content's interface to server-sent-events. An EventSource instance opens a persistent connection to an Http server.

Now client makes a get Http request to the endpoint with the following headers

accept: text/event-stream

cache-control: no-cache

Connection: keep-alive

Now the server should confirm the subscription with the following headers.

content-type: text/event-stream;charset=UTF-8

After the subscription is done the server can send the server events. And the connection between the client and the server will only close either there is timeout on the server side or we manually closed it from client side. We will see both the cases in our code.

Why we need Server Sent Events ?

Server Sent Events are a standard allowing browser clients to receive a stream of updates from a server over a HTTP connection without resorting to polling. Now unlike websockets ,they provide one way communication that is from server to client side communication. And to be honest it depends on your requirements that you want to use. Suppose if want to make chatbot which requires two may communication then in that case we can use websockets. And consider the case where you want to track the stock prices and social media followers where from server you are sending streams of data then you can go ahead and use server sent events.

Alternatives to Server Sent Events

Now as we know Server Sent Events is Server push type of communication in which you push data from the server side. Now the alternative that we can use in place of Server Sent Events is Websockets . But fellas its all depends on your requirement and choose accordingly which one to use in your project.

Implementation of Server Sent Events

Now we will look at both the client and server side code.

Client Side

//for subsciption to the server
var source = new EventSource(endpoint);
// now what do to when we recieve a new event from server side
source.onmessage = function (event) {
alert(event.data);
};
// We want to stop receiving events now
eventSource.close()

The above piece of code do three things. First it helps in subscribing to the server or creating a connection between the client and server. Second what to do in case if server pushes a new event. Third to close the connection if you no longer want to receive the events.

Server Side

@RestController
@RequestMapping
public class EventController {
@GetMapping("/events")
@CrossOrigin
public SseEmitter sendOrderEvent() throws Exception {
final SseEmitter sseEmitter = new SseEmitter(0L);//on time of completion
sseEmitter.onCompletion(() -> logger.info("SseEmitter is completed"));
// in case of timeout
sseEmitter.onTimeout(() -> logger.info("SseEmitter is timed out"));
sseEmitter.execute(() -> {
try {
for(int i=0;i<100;i++){
emitter.send("hello"+i);
logger.info("emit:{}","hello"+i);
Thread.sleep(1000*1);
}
emitter.complete();
} catch (Exception e) {
emitter.completeWithEror(e);
}
});
}}

Now the argument in SseEmitter() specifies the no of milliseconds for which you want to send the events or open the connection. In our case we are using 0L . Which means there is no timeout. But you need to be careful what you use here.

So here we are using asynchronization for pushing events to the client side .In our case we are iterating over the loop and after the iterations are completed the emitter.complete() method will cause the connection to be close between the the client and server.

Exceptions while implementing SseEmitter

Now consider a case where you are setting a finite timeout in your SseEmitter() eg: 5000 milliseconds . And if the time is exhausted before the completion of the emitter task(our for loop iterations) an AyncRequestTimeoutException will be thrown and the reason for this is because when you use spring boot with embedded tomcat server, specially when you break the connection of the event stream with the server by manually reloading the browser page. In your server side timeout handler you may be marking the stream as complete but still there is a dispatch made to the MVC which is not handled by default and results in this error.

Now to avoid this exception you can use 0L as time in constructor. Or you can use exception Handler to handle this.


@ExceptionHandler
public void exceptionHandler(AyncRequestTimeoutException e){
logger.error(e.message());
}

Resources Used

--

--

Responses (1)