Using Server Sent Event with Future in Tornado

Some background

Server Sent Event (sse)

Server-Sent Events are real-time events emitted by the server and received by the browser. They’re similar to WebSockets in that they happen in real time, but they’re very much a one-way communication method from the server.

http://html5doctor.com/server-sent-events/

In other words, server sent events is a one way communication channel from a server to its clients. To communicate from browser to server, we use normal HTTP requests.

Tornado Web Framework

Tornado is an asynchronous Python web framework. Tornado applies the concept of an IO loop similarly found in NodeJS without sacrificing the power of multithreading. Developers are not forced to do everything in one and only one thread or fork heavy child processes. Tornado's version of asynchronous flow does not rely on callbacks but Python's future. Together with yield, future allows developers to write straight forward application logic. Code is written in a synchronous style but is executed in an asynchronous flow. See a comparison in my blog post here.

# module data access
# executor is an instance of ThreadPoolExecutor
def get_data():  
    future = Future()
    def resolve():
       time.sleep(10) # blocks  current tbread
       future.set_result(123)
    executor.submit(resolve)
    return future
# some consumer module
future = get_data()  
# perform some long-running task without waiting for the future to be resolved
data = yield future  
#Here we wait for the future to be resolved after 10s.  If data is not ready, execution is blocked, otherwise things keep moving.

A demo

To explore the capability of Tornado web framework in terms of holding multiple long connections, I have built a simple weather tracking web app which periodically fetches live weather information in Singapore and pushes to all connected clients. Check it out on your mobile device! It may be useful if you happen to travel to Singapore - it rains on and off every day. CLICK HERE to see the app

How does the app work? On client side, I simply use the native EventSource class to connect to the endpoint /events and listen to incoming data packets.

var source = new EventSource('/events');  
source.onmessage = function (message) {  
  // update view based on message.data
  // var data = JSON.parse(message.data);
}

For server side, it is just a bit more complex. The IOLoop instance listens and handle incoming requests on the main thread. This same loop also checks whether updated data has been pushed to each client or not before actually pushing it to the clients. However, a worker thread is spawned (using an instance of ThreadPoolExecutor) to periodically poll the weather service API for recent data. The design ensures that:

  • I can control the number of API requests
  • My server can provide fresh data to a nearest arbitrary interval
  • Clients only receive updated data, not repeated data

Application Control Flow

A point of interest is that there are only two threads running in the application process - no matter how many client connections.

You can find the source code at https://github.com/khanhhua/singaporeweather