I've been building a web backend for an iPhone app with it in the past two months. It's ideal for serving simple JSON requests that depend on other I/O stuff (like, contacting Facebook) - but it's got plenty of problems:
1. The callback architecture. I simply freakin' hate the IOLoop. If you have just one asynchronous I/O operation, it's nice and easy. If you have one that depends on another, you have to wrap and curry a callback inside another one. Three? More wrapping and currying. I understand it's pretty much the same in many other platforms like client side AJAX these days. But man, this is Python and we DO have far better alternatives like gevent! You want the epoll() and kqueue() for performance? Fine. But it's Python and you can do it with coroutines without all that asynchronous event handling mess. And minus all those function wrapping and curry, I'm sure it can be much faster, all the while being easier to program with.
2. The Facebook API in auth.py sucks. Just read its source code - it performs all requests by GET - WTF?! It can't even upload files unless you hack it (I've got a branch at http://github.com/martinkou/tornado-upload for that). And look at that _signature function under the FacebookMixin class - it doesn't even use session secrets? I'm not sure if I'm doing anything wrong, and Facebook API's docs don't mention that.. but in my testing, if I just use the application secret key all the way for the signature, Facebook gives me 104 errors. I had to hack it again to make it use session secrets in signing requests.
I don't really care about databases. But last time I checked, psycopg2 has a page telling you how to do async requests with gevent or libevent style coroutines. Tornado? Good luck integrating that with IOLoop and currying your callbacks. Sure you can still make it work, but it's unnecessarily complicated.
btw, it's possible to run Tornado on $NUM_CPU forked processes, have them listen to one port and so you wouldn't need nginx load balancing. Just read httpserver.py and you'll find a fork() there. The downside? Assuming you're still running your Tornado in your console and haven't daemonized it somehow - you press Ctrl-C, it exits (the console part, I mean) - and the $NUM_CPU children are still running. It's still not fixed today, and that doesn't really speak of quality.
I like Tornado, too, but here are a couple problems:
- Hard to test, at least relative to Django. There's no built-in support, and being async makes it a little complicated. I have it working now, but it took some bashing and plenty of the mock library.
- Lack of libraries for things we might want to do asynchronously. I can buy the argument that that's fine for databases, but for AMQP it's mandatory. The lack of a (working) Tornado AMQP library drove me to use Cyclone, which is a production-ready port of Tornado to the Twisted framework. Libraries ahoy.
I had a few issues with Tornado when I tried it (back in February). They pretty much came down to two things:
1. Database queries are not asynchronous. This is a big deal for most traditional web applications, and pretty much removes any benefit to using Tornado for them. Even for things that rely mostly on HTTP APIs, there's still a decent chance that you'll want to do authentication or something against a database.
2. It was generally lacking a lot of the maturity and handy declarative syntax you can get with frameworks like Django. There was no ORM, form helpers, etc.
That being said, I really like the idea, performance was great and development seemed to be proceeding at a tremendous pace. The whole project is on GitHub and they seemed very willing to accept both bug-fixes and features.
re 1: Bret Taylor addressed db queries in the video on Tornado (the video is on Facebook, but I cannot find the link). If you run more than one instance of Tornado per core and you have a decent load balancer in front of the instances, then short blocking calls to the db work just fine. If your db calls are taking a long time, then it might be better to speed up the queries with caching or query optimization before worrying about async.
I like this approach because I prefer writing app logic in a straight line instead of in callbacks. The code is easier to understand and debug.
Exactly. Async is very important when you're blocking on an external event (such as fetching a page doing doing hanging-GET), but local db or filesystem blocking can most easily be handled by just running more Tornado instances (because they better not be blocking for too long anyway, or else your site is dead).
You might be able to do this with decorators in Python, but in Ruby (1.9 or 1.8 w/ the fibers patch) there are libraries that make these calls asynchronous with minimal or no changes to your code, e.g., neverblock.
Tornado is great because it's so damn simple. Just a couple of nice little libraries that work well together. It doesn't try to be sneaky about anything, so a glance at the code on Github is usually enough to determine what's going on behind the scenes, and whether or not it's going to be a problem.
I've built a few small sites with it, and it's great for experimenting with custom architectures. It doesn't really assume anything about your data or architecture, so you're free to build things on top of it. If you want extra features, they need to come from somewhere else. If you want an integrated solution to knock out a CRUD website in a weekend, you should look at something like Django (which is also fantastic).
I use supervisord for maintaining multiple instances of the application running. I found it the simplest way to manage it. It also gave me support for watching and restarting any instances that hang. I'm also using nginx to proxy. Currently I'm proxying 2 instance of tornado on a 4 core box. I'm also running mongodb, memcached, and nginx on the same small 256MB ram box right now.
In the future I could see me moving to node.js in the future, but for any application that requires working with multiple APIs, Tornado was a much better headstart, and the framework itself was more mature and ready to use than anything from the node.js camp several months ago.
Unfortunately no one is actively supporting the auth portion of Tornado, and it could use a maintainer. I'd set up, but with a newborn I'm not sure when I'll get to it. I'll be releasing ( and have already done so in the past ) any changes I make back to the Tornado project.
I've been building things with tornado and pymongo for going on six months, absolutely love it. Light and fast, easy to customize, a solid auth system builtin, a realtime chat demo (!); keep things simple and it is a joy.