<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Perplexed Labs &#187; tornado</title>
	<atom:link href="http://blog.perplexedlabs.com/tag/tornado/feed/" rel="self" type="application/rss+xml" />
	<link>http://blog.perplexedlabs.com</link>
	<description>web development war stories from the frontlines to the backend</description>
	<lastBuildDate>Mon, 16 May 2011 14:19:38 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.1.2</generator>
		<item>
		<title>Async DNS Resolution in Tornado&#8217;s AsyncHttpClient (curl multi, c-ares)</title>
		<link>http://blog.perplexedlabs.com/2010/11/01/asynchronous-dns-resolution-in-tornados-asynchttpclient-curl-multi-c-ares/</link>
		<comments>http://blog.perplexedlabs.com/2010/11/01/asynchronous-dns-resolution-in-tornados-asynchttpclient-curl-multi-c-ares/#comments</comments>
		<pubDate>Mon, 01 Nov 2010 23:50:26 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[async]]></category>
		<category><![CDATA[asynchronous]]></category>
		<category><![CDATA[asynchttpclient]]></category>
		<category><![CDATA[c-ares]]></category>
		<category><![CDATA[curl]]></category>
		<category><![CDATA[dns]]></category>
		<category><![CDATA[libcurl]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[tornado]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=513</guid>
		<description><![CDATA[I learned some rather important facts about cURL's multi interface (which makes it possible to perform asynchronous HTTP requests and what Python's Tornado framework uses under the hood in it's AsyncHttpClient helper). I was investigating some intermittent issues in an application at work - transient DNS issues were causing the application to become unresponsive. This [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/07/01/pythons-tornado-has-swept-me-off-my-feet/' rel='bookmark' title='Permanent Link: Python&#8217;s Tornado has swept me off my feet'>Python&#8217;s Tornado has swept me off my feet</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/07/24/tornado-1-0-released/' rel='bookmark' title='Permanent Link: Tornado 1.0 Released'>Tornado 1.0 Released</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/' rel='bookmark' title='Permanent Link: Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger'>Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>I learned some rather important facts about cURL's multi interface (which makes it possible to perform asynchronous HTTP requests and what Python's <a href="http://www.tornadoweb.org/">Tornado</a> framework uses under the hood in it's AsyncHttpClient helper).</p>
<p>I was investigating some intermittent issues in an application at work - transient DNS issues were causing the application to become unresponsive.  This was confusing at first because it was written to perform HTTP requests asynchronously.  The important thing here is that it was specifically DNS resolution that was failing.  As I dug deeper I realized that simple health checks, ones that did not perform any HTTP requests, were also hanging... something had to be blocking.</p>
<p>Taking a look at the Tornado source, unsurprisingly, it was leveraging the <a href="http://curl.haxx.se/libcurl/c/libcurl-multi.html">multi</a> functionality of libcurl (via pycurl).  What was surprising to me was that the DNS resolution portion of the multi interface, by default, blocks on non-windows installations.  Only when compiled with <a href="http://c-ares.haxx.se/">c-ares</a> support does it perform these async.</p>
<p>You learn something new every day!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/07/01/pythons-tornado-has-swept-me-off-my-feet/' rel='bookmark' title='Permanent Link: Python&#8217;s Tornado has swept me off my feet'>Python&#8217;s Tornado has swept me off my feet</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/07/24/tornado-1-0-released/' rel='bookmark' title='Permanent Link: Tornado 1.0 Released'>Tornado 1.0 Released</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/02/08/deployment-using-capistrano-and-webistrano-via-rails-and-phusion-passenger/' rel='bookmark' title='Permanent Link: Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger'>Deployment Using Capistrano / Webistrano via Rails / Phusion Passenger</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2010/11/01/asynchronous-dns-resolution-in-tornados-asynchttpclient-curl-multi-c-ares/feed/</wfw:commentRss>
		<slash:comments>1</slash:comments>
		</item>
		<item>
		<title>Python&#8217;s Tornado has swept me off my feet</title>
		<link>http://blog.perplexedlabs.com/2010/07/01/pythons-tornado-has-swept-me-off-my-feet/</link>
		<comments>http://blog.perplexedlabs.com/2010/07/01/pythons-tornado-has-swept-me-off-my-feet/#comments</comments>
		<pubDate>Thu, 01 Jul 2010 13:00:37 +0000</pubDate>
		<dc:creator>Matt</dc:creator>
				<category><![CDATA[Development]]></category>
		<category><![CDATA[Infrastructure]]></category>
		<category><![CDATA[Python]]></category>
		<category><![CDATA[API]]></category>
		<category><![CDATA[asynchronous]]></category>
		<category><![CDATA[non-blocking]]></category>
		<category><![CDATA[REST]]></category>
		<category><![CDATA[tornado]]></category>
		<category><![CDATA[web.py]]></category>

		<guid isPermaLink="false">http://blog.perplexedlabs.com/?p=457</guid>
		<description><![CDATA[I've been working with Python's Tornado for about 2 months now and I love it. Tornado is a non-blocking web server written in Python. It's structure is similar to web.py so users of that popular Python web framework will feel right at home. This is a structure that lends itself really well to developing RESTful [...]


Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/03/04/python-data-sharing-in-the-multiprocessing-module/' rel='bookmark' title='Permanent Link: Python data sharing in the multiprocessing module'>Python data sharing in the multiprocessing module</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/07/24/tornado-1-0-released/' rel='bookmark' title='Permanent Link: Tornado 1.0 Released'>Tornado 1.0 Released</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/11/01/asynchronous-dns-resolution-in-tornados-asynchttpclient-curl-multi-c-ares/' rel='bookmark' title='Permanent Link: Async DNS Resolution in Tornado&#8217;s AsyncHttpClient (curl multi, c-ares)'>Async DNS Resolution in Tornado&#8217;s AsyncHttpClient (curl multi, c-ares)</a></li>
</ol>]]></description>
			<content:encoded><![CDATA[<p>I've been working with Python's <a href="http://www.tornadoweb.org/">Tornado</a> for about 2 months now and I love it.</p>
<p>Tornado is a non-blocking web server written in Python.  It's structure is similar to web.py so users of that popular Python web framework will feel right at home.  This is a structure that lends itself really well to developing RESTful APIs as the methods you write to handle incoming requests are named after the HTTP methods used:</p>
<pre class="brush: python; title: ;">
class PlaceHandler(tornado.web.RequestHandler):
    def get(self, id):
        # respond to a GET
        self.write('GETting something')

    def post(self):
        # respond to a POST
        self.write('POSTing something')
</pre>
<p>You match URI paths to "handlers" (the <em>controller</em> for those MVC folk) via a list of regex, handler tuples that instantiate an "application".</p>
<pre class="brush: python; title: ;">
application = tornado.web.Application([
    (r&quot;/place&quot;, PlaceHandler),
    (r&quot;/place/([0-9]+)&quot;, PlaceHandler)
])

if __name__ == &quot;__main__&quot;:
    http_server = tornado.httpserver.HTTPServer(application)
    http_server.listen(8888)
    tornado.ioloop.IOLoop.instance().start()
</pre>
<p>As usual any values that are captured from the regex are passed, in order, to the method that receives the request in the handler.</p>
<p>Because of it's non-blocking nature Tornado bundles an asynchronous HTTP client for use internally.  Additional modules include a command line and config file convenience library, escaping, 3rd party authentication (Facebook, Twitter, etc.), a wrapper around MySQLdb, and templating.  All in all this makes it a formidable web framework in its own right, especially if you're looking for something that's light and <a href="http://www.tornadoweb.org/documentation#performance">FAST</a>.</p>
<p>In production, I'm running 4 Tornado instances per server behind <a href="http://nginx.org/">nginx</a>.</p>
<p>One issue not addressed out of the box was daemonizing the Tornado instance.  I added PID file management and the ability to daemonize as follows (pid.py module follows):</p>
<pre class="brush: python; title: ;">
# capture stdout/err in logfile
log_file = 'tornado.%s.log' % options.port
log = open(os.path.join(settings.log_path, log_file), 'a+')

# check pidfile
pidfile_path = settings.PIDFILE_PATH % options.port
pid.check(pidfile_path)

# daemonize
daemon_context = daemon.DaemonContext(stdout=log, stderr=log, working_directory='.')
with daemon_context:
    # write the pidfile
    pid.write(pidfile_path)

    # initialize the application
    http_server = tornado.httpserver.HTTPServer(application.app)
    http_server.listen(options.port, '127.0.0.1')

    try:
        # enter the Tornado IO loop
        tornado.ioloop.IOLoop.instance().start()
    finally:
        # ensure we remove the pidfile
        pid.remove(pidfile_path)
</pre>
<p>And now the pid.py module:</p>
<pre class="brush: python; title: ;">
# pid.py - module to help manage PID files
import os
import logging
import fcntl
import errno

def check(path):
    # try to read the pid from the pidfile
    try:
        logging.info(&quot;Checking pidfile '%s'&quot;, path)
        pid = int(open(path).read().strip())
    except IOError, (code, text):
        pid = None
        # re-raise if the error wasn't &quot;No such file or directory&quot;
        if code != errno.ENOENT:
            raise

    # try to kill the process
    try:
        if pid is not None:
            logging.info(&quot;Killing PID %s&quot;, pid)
            os.kill(pid, 9)
    except OSError, (code, text):
        # re-raise if the error wasn't &quot;No such process&quot;
        if code != errno.ESRCH:
            raise

def write(path):
    try:
        pid = os.getpid()
        pidfile = open(path, 'wb')
        # get a non-blocking exclusive lock
        fcntl.flock(pidfile.fileno(), fcntl.LOCK_EX | fcntl.LOCK_NB)
        # clear out the file
        pidfile.seek(0)
        pidfile.truncate(0)
        # write the pid
        pidfile.write(str(pid))
        logging.info(&quot;Writing PID %s to '%s'&quot;, pid, path)
    except:
        raise
    finally:
        try:
            pidfile.close()
        except:
            pass

def remove(path):
    try:
        # make sure we delete our pidfile
        logging.info(&quot;Removing pidfile '%s'&quot;, path)
        os.unlink(path)
    except:
        pass
</pre>
<p>I'm going to follow up this post another on how I added a simple concept of "models" and an easy way to perform MySQL transactions.  Let me know if you have any specific questions!</p>


<p>Related posts:<ol><li><a href='http://blog.perplexedlabs.com/2010/03/04/python-data-sharing-in-the-multiprocessing-module/' rel='bookmark' title='Permanent Link: Python data sharing in the multiprocessing module'>Python data sharing in the multiprocessing module</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/07/24/tornado-1-0-released/' rel='bookmark' title='Permanent Link: Tornado 1.0 Released'>Tornado 1.0 Released</a></li>
<li><a href='http://blog.perplexedlabs.com/2010/11/01/asynchronous-dns-resolution-in-tornados-asynchttpclient-curl-multi-c-ares/' rel='bookmark' title='Permanent Link: Async DNS Resolution in Tornado&#8217;s AsyncHttpClient (curl multi, c-ares)'>Async DNS Resolution in Tornado&#8217;s AsyncHttpClient (curl multi, c-ares)</a></li>
</ol></p>]]></content:encoded>
			<wfw:commentRss>http://blog.perplexedlabs.com/2010/07/01/pythons-tornado-has-swept-me-off-my-feet/feed/</wfw:commentRss>
		<slash:comments>17</slash:comments>
		</item>
	</channel>
</rss>

