DZone
Thanks for visiting DZone today,
Edit Profile
  • Manage Email Subscriptions
  • How to Post to DZone
  • Article Submission Guidelines
Sign Out View Profile
  • Post an Article
  • Manage My Drafts
Over 2 million developers have joined DZone.
Log In / Join
Refcards Trend Reports
Events Video Library
Refcards
Trend Reports

Events

View Events Video Library

Related

  • WebSocket vs. Server-Sent Events: Choosing the Best Real-Time Communication Protocol
  • Creating Real-Time Dashboards Using AWS OpenSearch, EventBridge, and WebSockets
  • Beyond Linguistics: Real-Time Domain Event Mapping with WebSocket and Spring Boot
  • Reactive Event Streaming Architecture With Kafka, Redis Streams, Spring Boot, and HTTP Server-Sent Events (SSE)

Trending

  • The Third Culture: Blending Teams With Different Management Models
  • No More Cheap Claude: 4 First Principles of Token Economics in 2026
  • Detecting Bugs and Vulnerabilities in Java With SonarQube
  • Working With Cowork: Don’t Be Confused
  1. DZone
  2. Coding
  3. Tools
  4. How to Build Real-Time Notification Service Using Server-Sent Events (SSE)

How to Build Real-Time Notification Service Using Server-Sent Events (SSE)

For the SSE to work, the server needs to tell the client that the response’s content-type is text/eventstream.

By 
Michal Kapiczynski user avatar
Michal Kapiczynski
·
Oct. 22, 20 · Tutorial
Likes (4)
Comment
Save
Tweet
Share
11.3K Views

Join the DZone community and get the full member experience.

Join For Free

Most of the communication on the Internet comes directly from the clients to the servers. The client usually sends a request, and the server responds to that request. It is known as a client-server model, and it works well in most cases. However, there are some scenarios in which the server needs to send a message to the client without the preceding request.

In such cases, we have a couple of options: we can use short and long polling, webhooks, websockets, or event streaming platforms like Kafka. However, there is another technology, not popularized enough, which in many cases, is just perfect for the job. This technology is the Server-Sent Events (SSE) standard.

What Are Server-Sent Events?

SSE definition states that it is an HTTP standard that allows a web application to handle a unidirectional event stream and receive updates whenever the server emits data. In simple terms, it is a mechanism for unidirectional event streaming.

Browsers Support

It is currently supported by all major browsers except Internet Explorer.

Message Format

The events are just a stream of UTF-8 encoded text data in a format defined by the Specification. The important aspect here is that the format defines the fields that the SSE message should have, but it does not mandate a specific type for the payload, leaving the freedom of choice to the users. 

Java
 




xxxxxxxxxx
1


 
1
{
2

          
3
 "id": "message id <optional>",
4

          
5
 "event": "event type <optional>",
6

          
7
 "data": "event data –plain text, JSON, XML… <mandatory>"
8

          
9
}



SSE Implementation

For the SSE to work, the server needs to tell the client that the response’s content-type is text/eventstream. Next, the server receives a regular HTTP request, and leaves the HTTP connection open until no more events are left or until the timeout occurs. If the timeout occurs before the client receives all the events it expects, it can use the built-in reconnection mechanism to re-establish the connection.

client and server

Simple Endpoint (Flux):

The simplest implementation of the SSE endpoint in Spring can be achieved by:

  • Specifying the produced media type as text/event-stream,
  • Returning Flux type, which is a reactive representation of a stream of events in Java.

@GetMapping(path = "/stream-flux", produces = MediaType.TEXT_EVENT_STREAM_VALUE)

public Flux<String> streamFlux() {

   return Flux.interval(Duration.ofSeconds(1))

           .map(sequence -> "Flux - " + LocalTime.now().toString());

ServerSentEvent Class

Spring introduced support for SSE specification in version 4.2 together with a ServerSentEvent class. The benefit here is that we can skip the text/event-stream media type explicit specification, as well as we can add metadata such as id or event type.

@GetMapping("/sse-flux-2")

public Flux<ServerSentEvent> sseFlux2() {

   return Flux.interval(Duration.ofSeconds(1))

           .map(sequence -> ServerSentEvent.builder()

                   .id(String.valueOf(sequence))

                   .event("EVENT_TYPE")

                   .data("SSE - " + LocalTime.now().toString())

                   .build());

}


SseEmitter Class:

However, the full power of SSE comes with the SseEmitter class. It allows for asynchronous processing and publishing of the events from other threads. What is more, it is possible to store the reference to SseEmitter and retrieve it on subsequent client calls. This provides a huge potential for building powerful notification scenarios.

@GetMapping("/sse-emitter")

public SseEmitter sseEmitter() {

   SseEmitter emitter = new SseEmitter();

   Executors.newSingleThreadExecutor().execute(() -> {

       try {

           for (int i = 0; true; i++) {

               SseEmitter.SseEventBuilder event = SseEmitter.event()

                       .id(String.valueOf(i))

                       .name("SSE_EMITTER_EVENT")

                       .data("SSE EMITTER - " + LocalTime.now().toString());

               emitter.send(event);

               Thread.sleep(1000);

           }

       } catch (Exception ex) {

           emitter.completeWithError(ex);

       }

   });

   return emitter;


Client example:

Here is a basic SSE client example written in Javascript. It simply defines an EventSource and subscribes to the message event stream in two different ways.

// Declare an EventSource

const eventSource = new EventSource('http://some.url');

// Handler for events without an event type specified

eventSource.onmessage = (e) => {

   // Do something - event data etc will be in e.data

};

// Handler for events of type 'eventType' only

eventSource.addEventListener('eventType', (e) => {

   // Do something - event data will be in e.data,

   // message will be of type 'eventType'

});


SSE vs. Websockets

When it comes to SSE, it is often compared to Websockets due to usage similarities between both of the technologies.

  • Both are capable of pushing data to the client,
  • Websockets are bidirectional – SSE unidirectional,
  • In practice, everything that can be done with SSE, and can also be achieved with Websockets,
  • SSE can be easier,
  • SSE is transported over a simple HTTP connection,
  • Websockets require full duplex-connection and servers to handle the protocol,
  • Some enterprise firewalls with packet inspection have trouble dealing with Websockets – for SSE that’s not the case,
  • SSE has a variety of features that Websockets lack by design, e.g., automatic reconnection, event ids,
  • Only Websockets can send both binary and UTF-8 data, SSE is limited to UTF-8, 
  • SSE suffers from a limitation to the maximum number of open connections (6 per browser + domain). The issue was marked as Won’t fix in Chrome and Firefox.

Use Cases:

use cases

Notification Service Example:

A controller providing a subscribe to events and a publish events endpoints.

Java
 




xxxxxxxxxx
1
45


 
1
@Slf4j
2

          
3
@RestController
4

          
5
@RequestMapping("/events")
6

          
7
@RequiredArgsConstructor
8

          
9
public class EventController {
10

          
11
   public static final String MEMBER_ID_HEADER = "MemberId";
12

          
13
 
14

          
15
   private final EmitterService emitterService;
16

          
17
   private final NotificationService notificationService;
18

          
19
 
20

          
21
   @GetMapping
22

          
23
   public SseEmitter subscribeToEvents(@RequestHeader(name = MEMBER_ID_HEADER) String memberId) {
24

          
25
       log.debug("Subscribing member with id {}", memberId);
26

          
27
       return emitterService.createEmitter(memberId);
28

          
29
   }
30

          
31
 
32

          
33
   @PostMapping
34

          
35
   @ResponseStatus(HttpStatus.ACCEPTED)
36

          
37
   public void publishEvent(@RequestHeader(name = MEMBER_ID_HEADER) String memberId, @RequestBody EventDto event) {
38

          
39
       log.debug("Publishing event {} for member with id {}", event, memberId);
40

          
41
       notificationService.sendNotification(memberId, event);
42

          
43
   }
44

          
45
}



A service for sending the events:

Java
 




xxxxxxxxxx
1
59


 
1
@Service
2

          
3
@Primary
4

          
5
@AllArgsConstructor
6

          
7
@Slf4j
8

          
9
public class SseNotificationService implements NotificationService {
10

          
11
 
12

          
13
   private final EmitterRepository emitterRepository;
14

          
15
   private final EventMapper eventMapper;
16

          
17
 
18

          
19
   @Override
20

          
21
   public void sendNotification(String memberId, EventDto event) {
22

          
23
       if (event == null) {
24

          
25
           log.debug("No server event to send to device.");
26

          
27
           return;
28

          
29
       }
30

          
31
       doSendNotification(memberId, event);
32

          
33
   }
34

          
35
 
36

          
37
   private void doSendNotification(String memberId, EventDto event) {
38

          
39
       emitterRepository.get(memberId).ifPresentOrElse(sseEmitter -> {
40

          
41
           try {
42

          
43
               log.debug("Sending event: {} for member: {}", event, memberId);
44

          
45
               sseEmitter.send(eventMapper.toSseEventBuilder(event));
46

          
47
           } catch (IOException | IllegalStateException e) {
48

          
49
               log.debug("Error while sending event: {} for member: {} - exception: {}", event, memberId, e);
50

          
51
               emitterRepository.remove(memberId);
52

          
53
           }
54

          
55
       }, () -> log.debug("No emitter for member {}", memberId));
56

          
57
   }
58

          
59
}



To sum up, Server-Sent Events standard is a great technology when it comes to a unidirectional stream of data and often can save us a lot of trouble compared to more complex approaches such as Websockets or distributed streaming platforms.

A full notification service example implemented with the use of Server-Sent Events can be found on my GitHub: https://github.com/mkapiczy/server-sent-events

Sources:

  • https://www.baeldung.com/spring-server-sent-events
  • https://www.w3.org/TR/eventsource/
  • https://stackoverflow.com/questions/5195452/websockets-vs-server-sent-events-eventsource
  • https://www.telerik.com/blogs/websockets-vs-server-sent-events
  • https://simonprickett.dev/a-look-at-server-sent-events/
Event Server-sent events Notification service Web Service WebSocket UTF-8 Build (game engine)

Published at DZone with permission of Michal Kapiczynski. See the original article here.

Opinions expressed by DZone contributors are their own.

Related

  • WebSocket vs. Server-Sent Events: Choosing the Best Real-Time Communication Protocol
  • Creating Real-Time Dashboards Using AWS OpenSearch, EventBridge, and WebSockets
  • Beyond Linguistics: Real-Time Domain Event Mapping with WebSocket and Spring Boot
  • Reactive Event Streaming Architecture With Kafka, Redis Streams, Spring Boot, and HTTP Server-Sent Events (SSE)

Partner Resources

×

Comments

The likes didn't load as expected. Please refresh the page and try again.

  • RSS
  • X
  • Facebook

ABOUT US

  • About DZone
  • Support and feedback
  • Community research

ADVERTISE

  • Advertise with DZone

CONTRIBUTE ON DZONE

  • Article Submission Guidelines
  • Become a Contributor
  • Core Program
  • Visit the Writers' Zone

LEGAL

  • Terms of Service
  • Privacy Policy

CONTACT US

  • 3343 Perimeter Hill Drive
  • Suite 215
  • Nashville, TN 37211
  • [email protected]

Let's be friends:

  • RSS
  • X
  • Facebook