Hi Friends,
Welcome to the 71st issue of the Polymathic Engineer newsletter.
This week, we will talk about a web technology widely used to send real-time updates from servers to clients: Server-Sent Event.
The outline will be as follows:
What is Server-Sent Events
Comparison SSE with polling, WebSocket, and WebHook
Implementing an SSE endpoint
Interesting Thoughts
What is Server-Sent Event?
Server-Sent Events (SSE) is a technology that allows servers to push notifications to the web browser or any client-side application in real-time, using a simple HTTP connection.
This server-push capability is handy for applications where it is crucial to deliver continuous data streams or real-time updates directly from the server to the client without the client having to request it periodically.
The SSE technology provides the following key features:
Real-Time Communication: Unlike the typical request-response model, SSE is based on a one-directional communication line from server to client. This makes it an ideal choice for applications requiring real-time updates, such as live sports scores, stock market feeds, data from IoT devices, or any other real-time monitoring system.
Built on Standard HTTP: SSE works through standard HTTP protocol and can be implemented in virtually any web application without complex infrastructure changes. It is initiated by a standard GET request from the client to the server, which looks like this:
GET /api/v1/live-updates Accept: text/event-stream Cache-Control: no-cache Connection: keep-alive
Here, the
Accept: text/event-stream
header tells the server that the client expects to receive continuous data streams.Efficiency and Performance: A persistent connection ensures that data is transmitted as soon as it's available, minimizing latency. The server does not close the connection after sending a response but keeps it open to send updates whenever new data arises.
Simplicity and Browser Support: SSE is straightforward to implement compared to other real-time data solutions like WebSockets, and all modern browsers like Firefox, Chrome, Opera, and Safari support it. The simplicity of SSE is evident by looking at its data format, where messages are encoded in UTF-8 text and structured as follows:
data: message content\n\n
Optional fields include
event
for specifying the type of event,id
to set custom identifiers for events, andretry
to configure automatic reconnection attempts in case of connection loss.
The following section will discuss how SSE compares to other real-time communication methods.
Comparison with polling, WebSocket, and WebHook
Several technologies enable a server to send real-time messages or updates to clients. Understanding how SSE differs from these technologies is crucial for making the right choice based on your application's specific needs.
Polling (Short and Long). In polling, the client repeatedly sends HTTP requests to the server to check for updates. Short polling does this at frequent intervals, while long polling holds the request open until an update is available or a timeout occurs. This method can be inefficient as it involves constant client-initiated communication. Unlike polling, SSE uses a single, long-held HTTP connection that remains open, allowing the server to push updates when they become available. SSE's main advantage over polling is its efficiency in maintaining an open line of communication without the constant overhead of new HTTP requests.
WebSockets. This technology provides a full-duplex communication channel that allows the client and server to send messages simultaneously over a single, long-lived connection. It's beneficial for applications that need bidirectional communication, such as live chat applications or collaborative platforms. In contrast, SSE is designed for unidirectional data flow, making it more suitable and straightforward for scenarios where the client does not need to send information back to the server. In addition, SSE is implemented over standard HTTP and does not require any protocol upgrade mechanism. This simplifies its adoption in web applications.
Webhooks. They are often used for server-to-server communication and allow a server to send real-time updates to another server. They work by making HTTP callbacks to external servers when certain events occur. This differs from SSE, which primarily focuses on server-to-client updates within web applications. SSE is optimal for directly updating the user interface of a web application without the complexities of managing callbacks to multiple external servers.
The comparative analysis shows that each technology has strengths, but the choice depends on your project's real-time communication needs. SSE offers one-way real-time updates from server to client, has low complexity, and is easy to use.
This makes it an excellent choice for web applications requiring live updates.
Implementing an SSE endpoint
Here’s the implementation of a simple SSE endpoint in .NET 8.
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
var builder = WebApplication.CreateBuilder(args);
builder.Services
.AddCors(options
=> options.AddPolicy(
name: "cors",
configurePolicy: policyBuilder =>
{
policyBuilder.AllowAnyHeader();
policyBuilder.AllowAnyMethod();
policyBuilder.AllowAnyOrigin();
}));
await using var app = builder.Build();
app.UseCors("cors");
app.MapGet(
pattern: "/sse",
requestDelegate: async context =>
{
context.Response.Headers.Add("Content-Type", "text/event-stream");
context.Response.Headers.Add("Cache-Control", "no-cache");
context.Response.Headers.Add("Connection", "keep-alive");
while (!context.RequestAborted.IsCancellationRequested)
{
await context.Response.WriteAsync($"{DateTimeOffset.Now}\n");
await context.Response.Body.FlushAsync();
await Task.Delay(1000);
}
});
await app.RunAsync();
Even if you’re unfamiliar with C# and .NET, the code is straightforward. While the request is not canceled, we wait for an update, serialize the message, and write it directly into the response buffer in the standard format.
We also make sure to include the Content-Type
, Cache-Contro, and Connection
headers.
The UpdateService
class is a Singleton simulating events that could be triggered from a separate process, like an event handler or a call from another service.
After writing a new update message to the buffer, we reset UpdateService
to wait for the next update.
On the client side, we use the standard EventSource
API, which is supported by all browsers and makes using an SSE endpoint pretty easy. The EventSource
instance establishes a persistent connection to an HTTP server to receive the events sent by the server in a text/event-stream format.
The connection between the client and the server will stay open until closed by calling the EventSource.close()
method. When you execute the application, the updates generated by your controller will be displayed in the user interface.
The complete code is available on my GitHub.
Interesting posts
A past experience as a software engineer is incredibly helpful to be a great EM. It helps you understand things, have empathy for the work and know what a fair request is. But you can certainly be a great EM even if you were an average engineer. Link
You must be good at doing things and communicating what you do. You'll never be appreciated enough for your efforts if you're merely good at doing things.
Blameless culture is very important in any organization. People should never feel afraid to accept responsibility for their failures. Everyone can make mistakes, what matters is how we learn from them. But you can’t learn if you aren’t honest about the why. Link