SockJS – WebSocket emulation
WebSocket technology is catching up, but it will take a while before
all browsers support it.
In the meantime there are loads of projects that aim to substitute for
WebSockets and enable 'realtime' capabilities for web apps. But all
attempts solve only a part of the general problem, and there isn't any
single solution that works, is scalable and doesn't require special
deployment tricks.
That's why a new project
was born:
SockJS - yet another
WebSocket emulation library, but this time done right.
SockJS has ambitious goals:
- Simple browser-side and server-side APIs, as close to WebSocket API
as possible. - Well documented scaling and load balancing techniques.
- Transports must fully support cross-domain communication.
- Transports must fall back gracefully in case of restrictive proxies.
- Connection establishment should be fast.
- No Flash on the client side, Javascript only.
- Client-side Javascript must be reasonably well tested.
- Additionally the server side code should be simple, in order to
lower the cost of writing servers for different languages.
Simple APIs
It may sound obvious, but the
WebSocket API is actually quite
good. It's an effect of a tremendous effort led by Ian Hickson and
others. It's shouldn't be forgotten that there were earlier, less
successful attempts to
achieve a similar thing -
the WebSockets API wasn't developed in the void.
Yet, I haven't seen any Javascript library that tries to emulate this
API closely. Early
Socket.io attempted that,
but it has evolved quite far away by now.
WebSocket doesn't define a server side API, but it's easy to come up
with a scheme with similar ideology and abstractions as the client
side.
Deployment story
SockJS does support cross-domain communication out of the box. You
can, and should, isolate SockJS server and host it on a different
domain than your main web site. There are multiple advantages of this
approach and, frankly speaking, it's just the only sane deployment
strategy.
Load balancing story
Single SockJS server capacity is finite. If you are anticipating that
a single server will not be enough for your needs - take a look at the
scaling scenarios below.
Use multiple domains for SockJS servers
The simplest solution is just to put every SockJS server under a
different domain name, for example sockjs1.example.com
and
sockjs2.example.com
, and allow clients to pick a server randomly.
Use a WebSocket-capable load balancer
You can choose to host all the SockJS traffic under one domain and use
a decent WebSocket-capable load balancer to split the traffic. There
is
a sample HAProxy configuration file
which can be a good starting point.
Use almost any load balancer
This is not a preferred solution, but it's possible to run scalable
SockJS even in environments where the load balancer doesn't support
WebSockets. Shared hosting providers are like that - for example
CloudFoundry. In order to make connection
establishment faster you can disable WebSocket protocol both on the
client and server side.
In such an environment load balancer must forward all requests for a
single SockJS session to a single SockJS server - the load balancer
must support sticky sessions (session affinity) in one of two
variants:
- Prefix-based sticky sessions. All requests to SockJS are prefixed
with a session id. Good loadbalancers may use that as a clue for
session-affinity algorithms (for example HAProxy can do it). -
JSESSIONID
cookie sticky sessions. By default SockJS server sets
this cookie. Some load balancers understand that cookie and
enable session stickyness (for example this is the case for
CloudFoundry).
Robust transport protocols
Apart from native WebSockets, SockJS comes with the support for few
carefully chosen transport protocols, and all of them support
cross-domain communication.
The basic idea is that there should be a decent streaming and polling
protocol for every browser. The polling ones must work work in
environments with restrictive proxies and support old browsers. There
are three ways every browser can establish connection:
Native WebSocket
WebSocket is the fastest and best transport protocol, it supports
cross-domain connections out of the box. Unfortunately it is not yet
widely supported by browsers. Also, some browsers may have problems
with proxies (for example, Firefox WebSocket implementation won't work
through the majority of the proxies). It's going to take some time
before the browser vendors agree on the protocol and proxy handling.
Streaming protocol
Streaming protocols supported by SockJS are based on http 1.1 chunking
- it allows browser to receive a single http response in many parts.
A great example of streaming protocol is
EventSource or streaming over
XHR (ajax). Messages sent from the browser are posted using another
XHR request.
Every browser supports a different set of streaming protocols and they
usually can't do cross-domain communication. Fortunately SockJS is
able to work around that limitation by using an Iframe and
communicating with it using an Html5 PostMessage API. This is quite
complex, but fortunately it is supported by the majority of the
browsers (with the exception of IE7).
Polling transport
SockJS supports few good-old polling protocols for ancient browsers
(including IE7). Unfortunately these techniques are quite slow, but
there is not much that can done about it.
Polling transports can also be used in situations where proxy on the
client side doesn't support WebSockets nor http chunking
- it is required for the streaming protocols.
Connection establishment should be fast
Opening SockJS connection should be fast, in some deployments it may
be necessary to establish a SockJS connection on every http page the
user visits.
If the browser supports it SockJS first tries to open a native WebSocket
connection. Depending on a network and server setup it may work or
fail. The failure should happen quite fast, unless the client is
behind a badly misbehaving proxy - in such case it can take up to 5
seconds to timeout.
After WebSocket transport is ruled out, SockJS opens XHR request that
intends to check if chunking is supported by the proxy. It's not that
unusual to meet proxies that don't support http chunking. Running
streaming protocol in such environment will fail with a timeout.
If chunking is working fine, SockJS chooses the best streaming
protocol supported by the browser. In the other case, polling
transports are used.
All that, depending on the browser, can take between 3 or 4 round trip
times from the browser to the server, plus a DNS request. Unless
you're behind a broken proxy or live in Antarctica it should be quite
fast.
This is one of the reasons why SockJS avoids using Flash transports -
Flash connection can take
at least to 3 seconds
if port 843 is blocked.
Client-side Javascript must be reasonably tested
SockJS is quite young and testing is not yet done properly. That said,
we have multiple end-to-end QUnit tests. At the moment are deployed in
few places:
- http://sockjs.popcnt.org/ (hosted in Europe)
- http://sockjs.cloudfoundry.com/ (CloudFoundry, websockets disabled, loadbalanced)
- https://sockjs.cloudfoundry.com/ (CloudFoundry SSL, websockets disabled, loadbalanced)
- http://sockjs.herokuapp.com/ (Heroku, websockets disabled)
Server side code should be simple
At that point SockJS-node implementation is using about 1200 lines of
code in CoffeeScript. About 340 are used by WebSocket protocol, 220 by
a simple http abstractions and only around 230 are used by core SockJS
logic.
The SockJS protocol used between the browser and the server is already
quite simple and we're working on making it even more obvious.
We do intend to support at least Node and Erlang servers and we would
be pleased to see Python and Ruby implementations as well. SockJS is
intended to be polyglot.
Summary
SockJS is quite young and there is loads of work remaining to be done,
but we believe it is stable enough for real applications. If you're
planning on doing realtime web apps, give it a try!
(Article also published on github pages)
Both comments and pings are currently closed.
3 Responses to “SockJS – WebSocket emulation”
How about a server in Java?
September 19th, 2011 at 5:05 pm
How about a server in Java? Seriously, there are different efforts to do real-time communication with the browser in java (various socket.io implementations, atmosphere), but they are all terrible to use.
Every time I start to read documentation of atmosphere, I find trying to figure out continuations, paus/resume/broadcast, annotations. Other implementations show a few lines of code as "simple" example of how to write java to send simple messages. The fact that all (?) of them need to be deployed into web/sevlet containers doesn't get described.
It took me less time to figure out what node.js was, what socket.io was, what npm was, how to install them, get them running and successfully communicate with browsers, than it did for me to get java solutions working. (actually I have NEVER gotten java solutions to work satisfactorily). This, despite the fact that I'm not a web developer and have almost no javascript experience...whereas I have been doing java for 10 years.
September 19th, 2011 at 5:12 pm
Why not Subscribe to SockJS mailing list and ask there http://groups.google.com/group/sockjs . I'm heard few people interested in doing SockJS-java!
September 21st, 2011 at 7:59 am
Have you looked at https://github.com/purplefox/node.x
It's a fully async general purpose application container for JVM languages. It supports TCP, HTTP, Websockets, etc, and many more modules on the way.
If you like, you can think of it as "node.js for JVM languages".
It's also a Rabbit/VMware project. (I work for VMware too ) )