Similar to Websockets, Server-Sent Events (SSE) is a technology that enables real-time communication between client and a server over HTTP. However, SSE is a unidirectional communication channel where the server can push data to the client without the client needing to request it. This makes it ideal for scenarios where the server needs to send real-time updates to the client, such as news feeds, stock tickers, or social media updates. Since SSE uses HTTP unlike Websockets, it is easier to implement and works well with existing web infrastructure. Also, it can share the same TCP connection with other HTTP requests, making it more efficient than Websockets in some scenarios.
Now let us see how SSE works.
Each message sent from the server to the client follows a specific structure and can include the following fields:
If the connection between the client and server is lost, the EventSource API can automatically attempt to reconnect. When reconnecting, the client sends the last received message ID in a header, allowing the server to resume sending messages from that point.
Sample Message
id: 12345 event: update retry: 10000 data: {"message": "New article published!"}
Now let us build real-time news feed application using Server-Sent Events in Spring Boot.
We will see how we can build real-time news feed application using Server-Sent Events using both the Spring Web and Spring Reactive Web. Let us create a new Spring Boot project with the following dependencies:
You can use Spring Initializr to generate the project.
pom.xml
)<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId> </dependency> </dependencies>
public record Article(String author, String title, String description, String url, String urlToImage, String publishedAt, String content) { } public record News(List<Article> articles) { }
In real world scenario, the news feed data can be fetched from external APIs or some middleware services. For simplicity, we will use a static list of news articles in a JSON file like below.
{ "totalResults": 2, "articles": [ { "source": { "name": "CNET" }, "author": "Kevin Lynch", "title": "Arsenal vs. Everton Livestream: How to Watch English Premier League Soccer From Anywhere - CNET", "description": "The Gunners need a win at home and hope for a Man City slip-up for their title dream to come true.", "url": "https://www.cnet.com/tech/services-and-software/arsenal-vs-everton-livestream-how-to-watch-english-premier-league-soccer-from-anywhere/", "urlToImage": "https://www.cnet.com/a/img/resize/47fe72d55cd7f1f105a65f5bc76f356701eeac01/hub/2024/05/18/8c2df860-58a6-41f8-9344-bf2c33f607af/gettyimages-2150177293.jpg?auto=webp&fit=crop&height=675&width=1200", "publishedAt": "2024-05-19T12:03:05Z", "content": "Arsenal come into the final game of the season against Everton at home with their hopes of winning the title for the first time in 20 years very much in the balance.\r\nWith Manchester City two points … [+5356 chars]" }, { "source": { "name": "CNET" }, "author": "Kevin Lynch", "title": "Crystal Palace vs. Aston Villa Livestream: How to Watch English Premier League Soccer From Anywhere - CNET", "description": "The Eagles look to maintain a strong finish to the season as they face Champions League-bound Villains.", "url": "https://www.cnet.com/tech/services-and-software/crystal-palace-vs-aston-villa-livestream-how-to-watch-english-premier-league-soccer-from-anywhere/", "urlToImage": "https://www.cnet.com/a/img/resize/02dee24e5eff30abe63262f841cab20b1d37c71e/hub/2024/05/18/3ee52c4f-8124-4cbc-b60a-3a8c6fa420a0/gettyimages-2152468104.jpg?auto=webp&fit=crop&height=675&width=1200", "publishedAt": "2024-05-19T12:03:08Z", "content": "Crystal Palace will look to continue their sparkling close-season form on Sunday as they host an Aston Villa side basking in the afterglow of Champions League qualification.\r\nPalace fans will likely … [+5318 chars]" }, ] }
@RestController public class NewsController { @GetMapping(value = "/news", produces = "text/event-stream") public Flux<ServerSentEvent<List<Article>>> news() throws IOException { Resource resource = new ClassPathResource("news.json"); Path path = resource.getFile().toPath(); String json = Files.readString(path); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); News news = objectMapper.readValue(json, News.class); return Flux.fromIterable(news.articles()).window(3) .delayElements(Duration.ofSeconds(1)) .flatMap(Flux::collectList) .map(articles -> ServerSentEvent.<List<Article>>builder() .event("news").data(articles).build()); } }
Flux
to represent a stream of Server-Sent Events. Web Flux provides a fluent way to create a stream of Server-Sent Events using the Flux
API.Flux
to window the news articles in a window of 3 and send it to the client every second.news
and sending the list of articles as data. You can set retry time and ID as well in the SSE message.Let us use httpie to test the news feed application.
http localhost:8080/news
You should see the news feed data being streamed to the client every second like below
http localhost:8080/news HTTP/1.1 200 Connection: keep-alive Content-Type: text/event-stream Date: Tue, 10 Dec 2024 07:23:58 GMT Keep-Alive: timeout=60 Transfer-Encoding: chunked // Message 1 event:news data:[ { "author": "Kevin Lynch", "content": "Arsenal come into the final game of the season against Everton at home with their hopes of winning the title for the first time in 20 years very much in the balance.\r\nWith Manchester City two points … [+5356 chars]", "description": "The Gunners need a win at home and hope for a Man City slip-up for their title dream to come true.", "publishedAt": "2024-05-19T12:03:05Z", "title": "Arsenal vs. Everton Livestream: How to Watch English Premier League Soccer From Anywhere - CNET", "url": "https://www.cnet.com/tech/services-and-software/arsenal-vs-everton-livestream-how-to-watch-english-premier-league-soccer-from-anywhere/", "urlToImage": "https://www.cnet.com/a/img/resize/47fe72d55cd7f1f105a65f5bc76f356701eeac01/hub/2024/05/18/8c2df860-58a6-41f8-9344-bf2c33f607af/gettyimages-2150177293.jpg?auto=webp&fit=crop&height=675&width=1200" }, { "author": "Kevin Lynch", "content": "Crystal Palace will look to continue their sparkling close-season form on Sunday as they host an Aston Villa side basking in the afterglow of Champions League qualification.\r\nPalace fans will likely … [+5318 chars]", "description": "The Eagles look to maintain a strong finish to the season as they face Champions League-bound Villains.", ... }, { "author": "Rob Price", "content": "Since its founding in 2020, WiFi Money has left a trail of lawsuits alleging fraud, bankruptcies, mental breakdowns, and financial devastation.Brandon Celi for BI\r\nAlex Moeller was having a great mon… [+24113 chars]", ... } ] // Message 2 event:news data:[ { "author": "Ryan Bittan", "content": "SALT LAKE CITY (ABC4) The defense for Kouri Richins the Kamas, Utah woman on trial for allegedly poisoning her husband with fentanyl, resulting in his death in March of 2022 has filed a motion to wit… [+1212 chars]", ... }, { "author": "Lauren Sforza", "content": "Sen. John Fetterman (D-Penn.) responded Sunday to Rep. Alexandria Ocasio-Cortezs (D-N.Y.) criticism of his comments mocking the Jerry Springer-like chaos during a House hearing last week, where the p… [+2090 chars]", ... }, { "author": "Perri Ormont Blumberg", "content": "For about 30 summers, Mindy Haar worked as the head lifeguard at a sleepaway camp in the Catskill Mountains. The teenage lifeguards under her charge wouldnt always remember to drink water, so she rem… [+10389 chars]", ... } ] // Message 3 event:news data:[ { "author": "Josh Dubow / AP", "content": "Jim Otto, the Hall of Fame center known as Mr. Raider for his durability through a litany of injuries, has died, the team confirmed Sunday night. He was 86.\r\nThe cause of death was not immediately kn… [+4229 chars]", ... }, { "author": "Anna Nordberg", "content": "During the darkest depths of COVID, my husband and I had the same conversation over and over. I predicted the iron grip of Activities Culture would loosen after lockdown, and parents would become mor… [+17909 chars]", ... }, { "author": "Elizabeth Merrill", "content": "Five years of football dominance in Kansas City inspired a spike in dogs named Kelce, and, for the creative types, some feline Catrick Mahomeses. But in Louisburg, Kansas, Shelly Trester and her fami… [+19763 chars]", ... } ]
If you open the URL in browser and check in dev tools, you can see the news feed data being streamed to the client every second.
private final ExecutorService nonBlockingService = Executors.newCachedThreadPool(); @GetMapping(value = "/news-emitter") public SseEmitter newsEmitter() throws IOException { SseEmitter emitter = new SseEmitter(); Resource resource = new ClassPathResource("news.json"); Path path = resource.getFile().toPath(); String json = Files.readString(path); ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); News news = objectMapper.readValue(json, News.class); nonBlockingService.execute(() -> { try { news.articles().stream().gather(Gatherers.windowFixed(3)).forEach(articles -> { try { emitter.send(articles); Thread.sleep(1000); } catch (Exception e) { emitter.completeWithError(e); } }); emitter.complete(); } catch (Exception ex) { emitter.completeWithError(ex); } }); return emitter; }
ExecutorService
to send the news feed data to the client every second.SseEmitter
to send the news feed data to the client.Now let us create a simple HTML page to display the news feed data being streamed from the server.
<!DOCTYPE html> <html> <head> <title>News Feed</title> </head> <body> <div id="news"></div> <script> var source = new EventSource('/news-emitter'); source.onmessage = function(event) { var articles = JSON.parse(event.data); var newsDiv = document.getElementById('news'); articles.forEach(function(article) { var articleDiv = document.createElement('div'); articleDiv.className = 'article'; var author = document.createElement('p'); author.textContent = 'Author: ' + article.author; articleDiv.appendChild(author); var title = document.createElement('h2'); title.textContent = article.title; articleDiv.appendChild(title); var description = document.createElement('p'); description.textContent = article.description; articleDiv.appendChild(description); var image = document.createElement('img'); image.src = article.urlToImage; articleDiv.appendChild(image); newsDiv.insertBefore(articleDiv, newsDiv.firstChild); }); }; </script> </body> </html>
EventSource
API to connect to the server and receive the news feed data.If we keep this HTML file with name ss.html
in resources/static folder, we can access it using URL http://localhost:8080/sse.html
and see the news feed data being streamed to the client every second.
Server-Sent Events (SSE) provide a simple and efficient way to implement real-time communication between the server and the client over HTTP. While SSE is not suitable for bidirectional communication, it is well-suited for scenarios where the server needs to send real-time updates to the client without the need for the client to request them.
To stay updated with the latest updates in Java and Spring follow us on youtube, linked in and medium. You can find the code used in this blog here
You can watch the video version of this blog on our youtube channel.
Learn how to build real-time applications using WebSockets in Spring Boot. This guide covers both simple WebSocket implementation and STOMP-based messaging, with practical examples of building a chat application.
Learn how to build an AI-powered stock portfolio advisor using Java, Spring Boot, LangChain4j, and OpenAI/Ollama. This guide walks you through integrating AI into your application to provide real-time investment advice based on the latest stock data.
Get instant AI-powered summaries of YouTube videos and websites. Save time while enhancing your learning experience.