HTML SSE with Node.js and Nginx: AlphaParty

HTML SSE (server sent events) are a somewhat obscure way to handle server to client communications that's often overlooked in favour of web sockets.  For a lot of things, web sockets are complete overkill, and you can get away with a simple SSE implementation.

For a future app I have planned, I need to support a variety of clients, web, Mac, PC, iOS.  They need to communicate in realtime with a server.  However, the communication requirements are pretty simple, and I don't want to spend all my time debugging it.  Websockets seems like overkill, so I looked for something easier.  I read about HTML SSE (Server Sent Events), and it sounded promising. I did some experimenting with SSE, and wound up making an AppleTV party game as a proof of concept that syncs multiple web clients in realtime. 

The exact details of using SSE isn't what I'm trying to describe here, for that, you can check out this article. It's pretty easy to follow and has instructions for both php and node (although, as I'll explain later, node is probably a better choice if you want your application to scale well).  The code for SSE is pretty easy, what I had trouble with was the server configuration, that's what I'm trying to address here.

If you're not a programmer, or not suffering from insomnia, you can probably stop reading here.  Just go to the AppleTV App Store and download AlphaParty and enjoy!  It's an easy to play party game up to 36 people can play simultaneously via phone using just the browser. I put it on sale for $2 USD (regularly $5) for the rest of June 2017.

SSE is basically an HTTP connection to the server that doesn't close.  When the server needs to alert the client(s) about something, it sends some additional data.  The data is essentially just a line of text that can contain whatever you want (probably JSON if you're sending anything interesting, but can be anything, really).

Any supported client (including all modern-ish web browsers) support receiving these, and will generate a Javascript event.  You can read about how to do it, and understand it fully pretty quick.  There are lightweight implementations in swift and other languages that generate native events or call blocks or whatever, so it's simple to implement in any platform.

You probably don't want to implement this in a PHP/Apache environment if you're planning to support many users.  The way Apache creates another thread for each connection becomes problematic because SSE can leave the connection open indefinitely.

I built the backend in Node.js, which is more suited to this sort of thing due to its non-blocking architecture.  It works great...  however, as you know if you're familiar with Node.js, you don't generally put your Node.js apps right on the internet.  If you want HTTPS and to handle many different applications on the same server, you'll want to proxy it behind a web server, either Apache or Nginx.  As mentioned before Apache's architecture makes it unsuitable for SSE because having many connections that are open for a (potentially) long time will cause it to have many threads to manage.  This leaves Nginx as the obvious choice.

However, there are some issues.  Out of the box, Nginx does not play nice with SSE.  There's 2 fundamental issues to contend with.

Aggregating

By default, Nginx won't send data immediately.  It waits a bit to see if there's more coming, so it can bundle it into a single packet.  This is definitely not ideal for SSE, where you send small bits of data, but expect them to be delivered immediately.

Connection Closing

This doesn't seem to be a problem if you're not using HTTPS, but if you are, Nginx likes to close connections that are idle for more than a few seconds.  Since SSE's modus operandi is to leave connections open and stream data when there's an event, Nginx winds up closing your SSE connection at inopportune times.  SSE has a reconnect setting, but there's a delay, and when trying to do anything approaching real-time, this becomes a problem.

Both of these are easily overcome by changing Nginx's configuration.. add the following lines to your "sites-available", in the location section for the Node.js server that uses SSE

#This deals with the Aggregating issue
    chunked_transfer_encoding off;
    proxy_buffering off;
    proxy_cache off;

#This deals with the connection closing issue
    proxy_set_header Connection keep-alive;
    proxy_connect_timeout 3600;
    proxy_send_timeout 3600;
    proxy_read_timeout 3600;
    keepalive_timeout 3600;

Thanks for reading... if you'd like to try my proof-of-concept AppleTV game that's implemented using HTML SSE, you can read more and get it from here, on sale for the rest of June 2017.  Send me a note at electrollama@gmail.com if you have any questions.

 

Tags ,