Push : SSE vs WebSockets

As we presented our new product streamdata.io at DevoxxFr, we were often asked why we choose Server-Sent Event (SSE) over WebSockets as our Push protocol. This post may help you understand our choice, and evaluate what best suits your needs. We’ll start first with a short description of the two protocols.

 

kangaroos

 

SSE: Server-Sent Events

HTTP based API dedicated to Push.

Implemented in recent browsers (Firefox, Chrome, Safari & Opera).

Allows the server to send data to client (one way communication)

http://en.wikipedia.org/wiki/Server-sent_events

http://www.w3.org/TR/eventsource/

WebSockets

TCP based protocol providing full duplex communication link between client and server.

http://en.wikipedia.org/wiki/WebSocket

http://www.w3.org/TR/websockets/

The problematic

At the beginning of the development of the Proxy, we had chosen to use SSE to communicate with the client for several reasons:

At first glance more suitable because once the connection is established, the client does not need to send data to the server. The bi-directional link is not useful in our case

Easier to implement because, unlike the WebSockets, SSE does not need to define a message exchange protocol (onOpen, onMessage, …)

SSE being based on HTTP, it is more compliant with various elements of existing IT infrastructure (load balancers, firewalls, …)

However, during the development, we encountered several problems related to the use of SSE:

Unable to add specific headers with Javascript SSE librairies

Unable to detect the disconnection of a client before trying to send data

Headers issue

In order to route the requests to the Information System, the proxy must be able to forward the headers of the requests (port number, OAuth authentication, …). In addition, to secure the transactions going through the proxy, we will setup an authentication mechanism based on security tokens that will also be routed to the Proxy.

Yet, the current implementations of EventSource object in JavaScript (base object for SSE communication) does not allow to override the headers. This problem does not exist with native SSE libraries (Java or ObjectiveC).

Disconnection detection

When a client is connected to the server via SSE, the server is not notified if the client disconnects. Disconnection will be detected by the server only when trying to send data to the client, and getting an error report mentioning that the connection was lost.

This is a problem in our case because the server performs regular pollings to the information system, and we want to stop this polling at the earliest to avoid inducing unnecessary load on the IS when no client is connected. In some cases, if the data source is not very dynamic, it may take several pollings before the data is changed, so that the server tries to send a diff to client.

This is problematic, so we considered different solutions which will be presented next.

Considered options

SSE with Polyfill

When using SSE in Javascript, we depend on the browser implementation. At the time writing this blog post, SSE is supported by Firefox, Chrome, Safari and Opera but not Internet Explorer (see http://caniuse.com/#search=Server)

A solution to override this implementation is to write a polyfill (http://en.wikipedia.org/wiki/Polyfill).

Today, there are several open-source polyfills that provide some fallback mechanisms to support SSE over all browsers, even those that don’t provide a native implementation. One of the most widely used among those pollyfills redefines the SSE implementation even for browsers that supports it natively, and replaces it with long polling.

We do not want such an implementation because in the case of a source whose data changes very frequently, long polling is less efficient than the standard polling strategy.

If we go for this solution, we will therefore write our own polyfill that uses the native implementation when it is available, and fallback mechanisms when it is not.

Pros
  • Rather simple to implement. We can rely on existing polyfill to implement the logic that suits us.
  • As we offer a Javascript SDK facilitating the use of the proxy in client applications, it is easy to include the polyfill in the SDK.
Cons
  • A polyfill can be hard to maintain with all browsers versions support.
SSE with query parameters

When browsing the web, we noticed that many Javascript developers were facing the same issue as us regarding headers and SSE, and most of them go for passing information into query parameters.

Pros
  • Very simple to implement
Cons
  • Adding query parameters will dramatically increase the size of URLs. But URLs can not be infinite. There is no universal maximum size, but it seems that 2048 characters is a reasonable limit.
  • Today we consider that this limit is not blocking. However, it will be noted in our documentation as a known limitation.
  • To avoid some code writing to users in order to concatenate all parameters in the URL, we will provide a minimalist Javascript SDK that will hide this complexity.
SSE with OPTIONS request

To avoid passing parameters to each request, one option would be that the client sends an OPTIONS request when first opening the connection, containing all the necessary settings for the subsequent requests. The proxy then would record these parameters and automatically add all requests from during the session.

Pros
  • Very simple to implement.
  • Cancels the constraint on URLs length as parameters are passed only once.
Cons
  • This introduces state-full sessions server-side, which can cause problems when we will need to replicate the Proxy installation on many servers to guarantee continuity of service and insure scalability.
Add a WebSocket proxy between streamdata.io Proxy and the client

The last option we considered was adding a WebSocket proxy in front of streamdata.io Proxy. The client communicates with the proxy in WebSocket. Then the WebSocket proxy communicates with streamadata.io proxy with the SSE. The WebSocket proxy has the ability to add headers as it uses the Java SSE library.

Pros
  • Once implemented, this proxy will be very simple to maintain.
  • WebSocket are more widely supported by web browsers than SSE.
  • The fallback mechanisms for browsers that do not support WebSockets are very easy to setup with SockJS.
  • The WebSocket proxy can detect client disconnections.
Cons
  • Quite complicated to implement.
  • Induces considerable complexity at the IT infrastructure level.
  • Requires to define a protocol for WebSocket communication between the client and the WebSocket proxy.
  • WebSocket connection does not allow to setup sticky URL load balancing strategy between client and WebSocket proxy.
  • WebSockets are not always supported by the firewall, proxy or load balancers. This will thus induce some constraint on the IT infrastructure.
How to address the disconnection detection issue.

To address this issue, we can set up a heartbeat mechanism sent by the server at regular intervals. With SSE, if you send a blank character, it does not induce network overload or additional processing at the client level, and simply will detect disconnection without waiting for data to be sent.

Adopted solution

First, we have implemented the solution using query parameters. The limitations to this solution are non-blocking for our beta version. It will simply be explained clearly in the documentation.

To simplify the use of the proxy for developers, a minimalist Javascript SDK is provided to mask the complexity of passing information as query parameters.

As we are providing a Javascript SDK, we could easily integrate a polyfill providing fallback mechanisms for (and only for) browsers that do not support SSE.

The disconnection detection issue is addressed with the heartbeat option.

Share it :
0000

Give it a try!

Try streaming any JSON REST API within 30 sec
curl -v "https://proxy.streamdata.io/http://mysite.com/myJsonRestService?param1=[]&param2=[]"

2 thoughts on “Push : SSE vs WebSockets

  • Hi,

    Thanks for this cool article.
    We are also having issues detecting when clients are disconnected. We had also adopted sending regular heartbeats to the clients, but for some clients , these heartbeats do not fail.
    We do not know yet how to explain it.
    Have you also seen something similar?

    Thanks

    • Hi!
      Thanks for your feedback. Detecting disconnections can be very tricky. We also in some cases noticed that the heatbeat sent by the server do not fail even when client is disconnected. To be more resilient to network outage, you can send heatbeat from both server and client. You can also use the SSE Last-Event-Id to make sure that client gets all the missed information upon reconnection.
      Cheers from the Streamdata.io team!

Leave a Reply

Your email address will not be published. Required fields are marked *