Server-Sent Events: The simplest realtime browser spec
Apr 3, 2014
By Julian Gruber
This is the first part of a series about our newest feature: the live debugger! Here is how we used the new Server-Sent Events standard to implement the real-time stream that helps you debug all of the calls going through our servers.
This is the first article in a series about how we built Debugger. To try it out, log in to segment.com, go to a project of yours and choose Debugger.
Why another spec?
In the web we unfortunately don’t have as many options for streaming network calls as with native apps, where you’d most likely just do raw tcp or udp.
The commonly used techniques are:
Polling Ajax / JSONP
Websockets
Flash
Server-Sent Events (SSE)
(Gifsockets ;))
Each have their pros and cons, and we could have successfully implemented Debugger with any of those. However many have big downsides, like polling creating too many connections, websockets requiring an HTTP upgrade and Flash being proprietary technology.
In the end we chose SSE because it’s the most simple and straight forward approach, both in concept and implementation, and does just what we need.
Server-Sent Events
I first learned about SSE from @konstantinhaase, who explained to me how Travis CI is using them for their live build updates. I was arguing why not to just use WebSockets, but in comparison WebSockets are a complex monstrosity and only shine when you need full 2 way communication.
All there is to SSE is the browser makes an HTTP request to an HTTP server which responds with lines prefixed by data:
and delimited by \n\n
. Boosh! There’s no weird HTTP upgrade, the server side can be dead simple and since it’s just HTTP it plays well with your existing stack of firewalls, webservers etc.
Unlike with polling you do still get persistent connections, so if you do auth logic on every request you only have to do that once per client.
An example SSE endpoint would respond like this:
It’s so simple you’ll wonder why SSE are still so little known.
The code to listen on the client is:
And here’s a server written in node.js, writing the current date every second. Don’t forget to set the Content-Type
header to text/event-stream
.
Advantages
The biggest advantage is how lightweight SSE are: It’s raw http and you can make it work in a few lines of code without using any libraries. For legacy browser support there are polyfills that work with pure JS.
Browser support
None of the Internet Explorers support SSE yet, and mobile Opera and Android don’t either. However, all recent versions of Firefox, Chrome, Safari and Opera do.
For the rest, there’s multiple polyfills. There’s one by Yaffle that’s smart about reconnecting and supports CORS, but also requires a modification to the server. The one by remy seems popular as well and is more lightweight but at the same time less robust.
Tuning reconnections
EventSource
s reconnect by default, but you can tune the reconnection behavior based on your app’s requirements, stack and expected clients:
Send a line of
retry: <milliseconds>
to configure the client’s reconnection time.Send a line of
id: <id>
before adata
line to associate an unique id with the event. The browser will keep track of that id and in case of a reconnection will addLast-Event-ID
as an HTTP header to let the server be smart about what to send the client straight away. For example, in a chat scenario you would send all the messages that happened since the last message the client has seen.
Custom event names
In case you’re sending different kinds of events, like a room join
and a message
event, SSE let’s you differentiate those in the stream. Send a line of event: <name>\n
before a data
line to let the EventSource know that’s what you’d like to call that event.
For example:
Will trigger:
Deployment
Some software has problems with long living connections, here’s what we had to tweak in our stack:
Add the
proxy_http_version 1.1;
directive to our nginx proxy so it doesn’t close connections too earlySend heartbeats every 30s because Amazon ELB kills idle connections after 60s
Helpful libraries
The segmentio/sse component is a nice wrapper for client side subscriptions. Install with component(1).
If your data source on the server side is a node stream, there’s the segmentio/sse-stream node module, a transform stream that converts to the SSE format:
Links
In the next installment of this series we’ll look at how live debugger is built upon generators and koa for fun and profit. Stay tuned!
The State of Personalization 2023
Our annual look at how attitudes, preferences, and experiences with personalization have evolved over the past year.
Get the reportThe State of Personalization 2023
Our annual look at how attitudes, preferences, and experiences with personalization have evolved over the past year.
Get the reportShare article
Recommended articles
How to accelerate time-to-value with a personalized customer onboarding campaign
To help businesses reach time-to-value faster, this blog explores how tools like Twilio Segment can be used to customize onboarding to activate users immediately, optimize engagement with real-time audiences, and utilize NPS for deeper customer insights.
Introducing Segment Community: A central hub to connect, learn, share and innovate
Dive into Segment's vibrant customer community, where you can connect with peers, gain exclusive insights, and elevate your success with expert guidance and resources!
Using ClickHouse to count unique users at scale
By implementing semantic sharding and optimizing filtering and grouping with ClickHouse, we transformed query times from minutes to seconds, ensuring efficient handling of high-volume journeys in production while paving the way for future enhancements.