Overview

This is an example of using the Flask web framework along with Web Sockets to create a simple client-server connection between back-end code (running on the server, in Python) and front-end code (running in the browser, in JavaScript).

It is the successor to my lpoll example of using long-polling (aka Comet, aka reverse AJAX) techniques to update client state quasi-asynchronously without the benefit of direct Web Socket support.

The benefits of shifting to Web Sockets are:

  • Faster updates and no waiting for updates. Lower latency user interactions are almost invariably better user interactions.
  • Lower overhead on server (fewer pointless "Has anything changed? No? Okay. How about now? No? Okay. How about now? ..." interactions)
  • Clearer knowledge, on both sides of the connection, as to what happened to the other half. If the server goes away, the onclose method is called, allowing the client to quasi-gracefully degrade in light of server failure.
  • Cleaner, simpler code. Concurrency is wrapped in a more appropriate way, meaning less low-level futzing for each side of the connection.

This example uses the same /update update URL as the lpoll to make comparing the two straightforward. A more Web Socket-focused example would probably do away with the distinction between update notifications and the subsequent data grab.

Most Python web frameworks use the Web Server Gateway Interface (WSGI) standard. WSGI defines a synchronous interface. That works well for the HTTP protocol used by static and templated Web pages, but doesn't readily accommodate the ad hoc, asynchronous communications needed for many real-time updates in fully interactive applications. The AJAX and Rich Internet Application (RIA) age wants more intimate, frequent client-server conversations as pages are incrementally rendered and regularly updated.

The solution is replacing the pure WSGI-based Web / HTTP serving underpinnings of Flask (aka Werkzeug) with a more threaded, asynchronous alternative. This example uses HTTP and Web Socket serving features based on the gevent quasi-threading / "greenlet" (really, coroutines) system. While gevent isn't truly multi-threaded, it creates the kind of time-sliced approximation of threading that makes the distinction academic for most local or reasonably low-volume Web apps.

Higher-volume apps might benefit from being hosted atop a different server; Flask and Web Sockets can be easily run atop the Tornado Web server, for instance. Though various benchmarks (e.g. this one) show gevent performance to be among the best.

See also:

Installation and Use

For Mac OS X, installing gevent and geventwebsocket from PyPI works great.:

hg clone https://jeunice@bitbucket.org/jeunice/flask-ws-example
cd flask-ws-example
sudo pip install -r requirements.txt
python serve.py

For Ubuntu Linux the version of gevent on PyPI doesn't seem to install nicely (at least not for version 13.04, "Raring Ringtail"). So a different procedure that installs the development version of gevent and a bunch of related dependencies is in order:

sudo apt-get install -y gcc python-pip git mercurial libev4 libev-dev libpython-dev
sudo pip install cython -e git://github.com/surfly/gevent.git@1.0rc2#egg=gevent
sudo pip install gevent-websocket flask
hg clone https://jeunice@bitbucket.org/jeunice/flask-ws-example
cd flask-ws-example
sudo python serve.py

Notes

  • This second release provides some nicer styling and shows a slightly enhanced UI (reporting how long it's been since the last update).
  • Note, this is in no way an sterling example of all things webapp. It uses CSS directly, rather than LESS or SCSS. It uses no template engine or update framework for JS code, when something like Ractive would make great sense. Updates double-bang, first with a Web socket "data is ready" and then a following HTTP request on /data. It displays times to the user in UTC, not local time. Et cetera. Adding those things would make this a more sophisticated and complete webapp example, but it would also arguably complicate understanding of the basics of getting the Web Socket connection working. I've tried to strike a balance between "enough" and "too much."
  • A version of this that uses the Web socket for both updates and data transmission is forthcoming.
  • The author, Jonathan Eunice or @jeunice on Twitter welcomes your comments and suggestions.