cover

Server-Side Events: Simple and Efficient Server Push

sorcererxw

Introduction

Usually, when we are choosing a backend push solution, what solution would we choose if we don't use WebSocket?

Let's start with the most basic Polling, where the client requests the server for updated data at regular intervals. However, this method cannot guarantee real-time data, and it also consumes a large amount of traffic.

Let's delve further. If there is no data update, the server should not respond to the client, but hold the connection until the data is updated. Then, the latest data is returned to the client, after which the client establishes a new connection. This is the mechanism of Long Polling. Compared to simple polling, it is more real-time, has less network overhead, but it's not very elegant as it requires constant reconnection.

So, can we keep the HTTP connection open when the server responds with data? All we need to do is to send all responses as a stream, and the client can continuously process the incoming events. This forms the basic principle of Server-Side Events (hereinafter referred to as SSE).

Compared to WebSocket, SSE is entirely based on HTTP, making it simpler and lighter for the server-side implementation.

Server-side Implementation

Below, I have implemented a simple SSE server using Golang+echo:

e:=echo.New()
e.GET("/sub", func(c echo.Context) error {
	c.Response().Header().Set(echo.HeaderContentType, "text/event-stream")
	c.Response().WriteHeader(http.StatusOK)
	
	for {
			select {
			case <-c.Request().Context().Done():
				return nil
			default:
			}
			fmt.Fprint(c.Response(), "data: hi\n\n")
			c.Response().Flush()
			time.Sleep(1 * time.Second)
	}
	return nil
})

Front-end Implementation

EventSource is a formal member of the W3C standard, and browsers provide a complete implementation for it. You only need to directly create an EventSource to subscribe to backend data.

const evtSrc = new EventSource("http://example.com/eventsource");
evtSrc.onmessage = function (event) {
	console.log(event.data)
}

In many cases, backend services or gateways will set a maximum connection duration for an HTTP request. Once this time limit is exceeded, the client's connection will be automatically disconnected. This requires the client to implement an automatic reconnection mechanism to ensure real-time data. However, EventSource has natively implemented an automatic reconnection mechanism. Once the connection is disconnected, the browser will continuously retry until the connection is restored.

Applicable Scenarios

From the above introduction, it can be seen that SSE is extremely friendly to both the front-end and the back-end, and is very easy to implement. It can very well meet the needs of event distribution. For message subscriptions displayed on the front-end, it is very suitable to use SSE for implementation.

Consuming streaming data
Tips for consuming streaming data.
https://developer.twitter.com/en/docs/tutorials/consuming-streaming-data

This is the Twitter developer documentation, which shows that Twitter's event push is also based on HTTP Stream implementation.