Well, what a piece of technology and every time you read more about it the better you know about it and can better appreciate it. Yes, I’m talking about the very own Tornado Web Server. My ‘attempt’ here is to tell you about the workflow of Tornado internals…
Tornado is a non blocking web server as we all know and understand. But how does it get this non blocking thing going its way?
Let’s first understand that Tornado is event driven web server. But what does event driven programming mean? Well it means your application thrives an event loop (single threaded) that keeps polling for events, identifying them as wanted and then handling them. Tornado also works on similar principles.
Tornado web server runs an ioloop, a single threaded main event loop and is at the crust of async nature of Tornado. ioloop.add_handler(fd, handlers, events) maintains a list of file descriptors, events to be watched and the corresponding handlers for each of these fd.
ioloop is a user space construct but who listens to events on the fd’s. That should be a kernel library and Tornado uses epoll, kqueue(BSD) – libraries that provide event notifications in a non-blocking way. epoll has three main functions:
- epoll_create – creates an epoll object
- epoll_ctl – controls the file descriptors and events to be watched
- epoll_watch – waits until registered event occurs or wait till timeout
epoll thus watches file descriptors (sockets) and returns needed (READ, WRITE & ERROR) events.
As described above, Tornado’s ioloop consumes these events (for the file descriptors) and run associated handlers for these events.
tornado.IOStream works as an abstraction layer on top of sockets. It provides three methods:
- read_until() – reads the socket until it finds empty line delimiter that suggests completion of HTTP headers
- read_bytes() – reads N number of bytes from socket
- write() – write a buffer to socket
All of these methods can call a callback when their job is done.
tornado.httpserver is a non blocking http server that accepts connections from clients on a defined port by adding the sockets to the ioloop.
- http_server = httpserver.HTTPServer(handle_request)
- http_server.listen(8888)
- ioloop.IOLoop.instance().start()
handler argument as mentioned in ioloop is a callback accepts the new connection, creates a IOStream, and creates a HTTPConnection object of httpserver class that is now responsible handling all client requests.