### Navigation
- [index](# "General Index")
- [modules](# "Python Module Index") |
- [next](# "Web framework") |
- [previous](# "Authentication and security") |
- [Tornado 4.4.dev1 documentation](#) »
- [User's guide](#) »
# Running and deploying
Since Tornado supplies its own HTTPServer, running and deploying it isa little different from other Python web frameworks. Instead ofconfiguring a WSGI container to find your application, you write a`main()` function that starts the server:
~~~
def main():
app = make_app()
app.listen(8888)
IOLoop.current().start()
if __name__ == '__main__':
main()
~~~
Configure your operating system or process manager to run this program tostart the server. Please note that it may be necessary to increase the numberof open files per process (to avoid “Too many open files”-Error).To raise this limit (setting it to 50000 for example) you can use the ulimit command,modify /etc/security/limits.conf or setting `minfds` in your supervisord config.
### Processes and ports
Due to the Python GIL (Global Interpreter Lock), it is necessary to runmultiple Python processes to take full advantage of multi-CPU machines.Typically it is best to run one process per CPU.
Tornado includes a built-in multi-process mode to start severalprocesses at once. This requires a slight alteration to the standardmain function:
~~~
def main():
app = make_app()
server = tornado.httpserver.HTTPServer(app)
server.bind(8888)
server.start(0) # forks one process per cpu
IOLoop.current().start()
~~~
This is the easiest way to start multiple processes and have them allshare the same port, although it has some limitations. First, eachchild process will have its own IOLoop, so it is important thatnothing touch the global IOLoop instance (even indirectly) before thefork. Second, it is difficult to do zero-downtime updates in this model.Finally, since all the processes share the same port it is more difficultto monitor them individually.
For more sophisticated deployments, it is recommended to start the processesindependently, and have each one listen on a different port.The “process groups” feature of [supervisord](http://www.supervisord.org) [http://www.supervisord.org]is one good way to arrange this. When each process uses a different port,an external load balancer such as HAProxy or nginx is usually neededto present a single address to outside visitors.
### Running behind a load balancer
When running behind a load balancer like nginx, it is recommended topass `xheaders=True` to the [`HTTPServer`](# "tornado.httpserver.HTTPServer") constructor. This will tellTornado to use headers like `X-Real-IP` to get the user's IP addressinstead of attributing all traffic to the balancer's IP address.
This is a barebones nginx config file that is structurally similar tothe one we use at FriendFeed. It assumes nginx and the Tornado serversare running on the same machine, and the four Tornado servers arerunning on ports 8000 - 8003:
~~~
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
use epoll;
}
http {
# Enumerate all the Tornado servers here
upstream frontends {
server 127.0.0.1:8000;
server 127.0.0.1:8001;
server 127.0.0.1:8002;
server 127.0.0.1:8003;
}
include /etc/nginx/mime.types;
default_type application/octet-stream;
access_log /var/log/nginx/access.log;
keepalive_timeout 65;
proxy_read_timeout 200;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
gzip on;
gzip_min_length 1000;
gzip_proxied any;
gzip_types text/plain text/html text/css text/xml
application/x-javascript application/xml
application/atom+xml text/javascript;
# Only retry if there was a communication error, not a timeout
# on the Tornado server (to avoid propagating "queries of death"
# to all frontends)
proxy_next_upstream error;
server {
listen 80;
# Allow file uploads
client_max_body_size 50M;
location ^~ /static/ {
root /var/www;
if ($query_string) {
expires max;
}
}
location = /favicon.ico {
rewrite (.*) /static/favicon.ico;
}
location = /robots.txt {
rewrite (.*) /static/robots.txt;
}
location / {
proxy_pass_header Server;
proxy_set_header Host $http_host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Scheme $scheme;
proxy_pass http://frontends;
}
}
}
~~~
### Static files and aggressive file caching
You can serve static files from Tornado by specifying the`static_path` setting in your application:
~~~
settings = {
"static_path": os.path.join(os.path.dirname(__file__), "static"),
"cookie_secret": "__TODO:_GENERATE_YOUR_OWN_RANDOM_VALUE_HERE__",
"login_url": "/login",
"xsrf_cookies": True,
}
application = tornado.web.Application([
(r"/", MainHandler),
(r"/login", LoginHandler),
(r"/(apple-touch-icon\.png)", tornado.web.StaticFileHandler,
dict(path=settings['static_path'])),
], **settings)
~~~
This setting will automatically make all requests that start with`/static/` serve from that static directory, e.g.,`http://localhost:8888/static/foo.png` will serve the file`foo.png` from the specified static directory. We also automaticallyserve `/robots.txt` and `/favicon.ico` from the static directory(even though they don't start with the `/static/` prefix).
In the above settings, we have explicitly configured Tornado to serve`apple-touch-icon.png` from the root with the [`StaticFileHandler`](# "tornado.web.StaticFileHandler"),though it is physically in the static file directory. (The capturinggroup in that regular expression is necessary to tell[`StaticFileHandler`](# "tornado.web.StaticFileHandler") the requested filename; recall that capturinggroups are passed to handlers as method arguments.) You could do thesame thing to serve e.g. `sitemap.xml` from the site root. Ofcourse, you can also avoid faking a root `apple-touch-icon.png` byusing the appropriate `<link />` tag in your HTML.
To improve performance, it is generally a good idea for browsers tocache static resources aggressively so browsers won't send unnecessary`If-Modified-Since` or `Etag` requests that might block therendering of the page. Tornado supports this out of the box with *staticcontent versioning*.
To use this feature, use the [`static_url`](# "tornado.web.RequestHandler.static_url") method inyour templates rather than typing the URL of the static file directlyin your HTML:
~~~
<html>
<head>
<title>FriendFeed - {{ _("Home") }}</title>
</head>
<body>
<div><img src="{{ static_url("images/logo.png") }}"/></div>
</body>
</html>
~~~
The `static_url()` function will translate that relative path to a URIthat looks like `/static/images/logo.png?v=aae54`. The `v` argumentis a hash of the content in `logo.png`, and its presence makes theTornado server send cache headers to the user's browser that will makethe browser cache the content indefinitely.
Since the `v` argument is based on the content of the file, if youupdate a file and restart your server, it will start sending a new `v`value, so the user's browser will automatically fetch the new file. Ifthe file's contents don't change, the browser will continue to use alocally cached copy without ever checking for updates on the server,significantly improving rendering performance.
In production, you probably want to serve static files from a moreoptimized static file server like [nginx](http://nginx.net/) [http://nginx.net/]. Youcan configure most any web server to recognize the version tags usedby `static_url()` and set caching headers accordingly. Here is therelevant portion of the nginx configuration we use at FriendFeed:
~~~
location /static/ {
root /var/friendfeed/static;
if ($query_string) {
expires max;
}
}
~~~
### Debug mode and automatic reloading
If you pass `debug=True` to the `Application` constructor, the appwill be run in debug/development mode. In this mode, several featuresintended for convenience while developing will be enabled (each of whichis also available as an individual flag; if both are specified theindividual flag takes precedence):
- `autoreload=True`: The app will watch for changes to its sourcefiles and reload itself when anything changes. This reduces the needto manually restart the server during development. However, certainfailures (such as syntax errors at import time) can still take theserver down in a way that debug mode cannot currently recover from.
- `compiled_template_cache=False`: Templates will not be cached.
- `static_hash_cache=False`: Static file hashes (used by the`static_url` function) will not be cached
- `serve_traceback=True`: When an exception in a [`RequestHandler`](# "tornado.web.RequestHandler")is not caught, an error page including a stack trace will begenerated.
Autoreload mode is not compatible with the multi-process mode of [`HTTPServer`](# "tornado.httpserver.HTTPServer").You must not give [`HTTPServer.start`](# "tornado.tcpserver.TCPServer.start") an argument other than 1 (orcall [`tornado.process.fork_processes`](# "tornado.process.fork_processes")) if you are using autoreload mode.
The automatic reloading feature of debug mode is available as astandalone module in [`tornado.autoreload`](# "tornado.autoreload"). The two can be used incombination to provide extra robustness against syntax errors: set`autoreload=True` within the app to detect changes while it is running,and start it with `python -m tornado.autoreload myserver.py` to catchany syntax errors or other errors at startup.
Reloading loses any Python interpreter command-line arguments (e.g. `-u`)because it re-executes Python using [`sys.executable`](https://docs.python.org/3.4/library/sys.html#sys.executable "(in Python v3.4)") [https://docs.python.org/3.4/library/sys.html#sys.executable] and [`sys.argv`](https://docs.python.org/3.4/library/sys.html#sys.argv "(in Python v3.4)") [https://docs.python.org/3.4/library/sys.html#sys.argv].Additionally, modifying these variables will cause reloading to behaveincorrectly.
On some platforms (including Windows and Mac OSX prior to 10.6), theprocess cannot be updated “in-place”, so when a code change isdetected the old server exits and a new one starts. This has beenknown to confuse some IDEs.
### WSGI and Google App Engine
Tornado is normally intended to be run on its own, without a WSGIcontainer. However, in some environments (such as Google App Engine),only WSGI is allowed and applications cannot run their own servers.In this case Tornado supports a limited mode of operation that doesnot support asynchronous operation but allows a subset of Tornado'sfunctionality in a WSGI-only environment. The features that arenot allowed in WSGI mode include coroutines, the `@asynchronous`decorator, [`AsyncHTTPClient`](# "tornado.httpclient.AsyncHTTPClient"), the `auth` module, and WebSockets.
You can convert a Tornado [`Application`](# "tornado.web.Application") to a WSGI applicationwith [`tornado.wsgi.WSGIAdapter`](# "tornado.wsgi.WSGIAdapter"). In this example, configureyour WSGI container to find the `application` object:
~~~
import tornado.web
import tornado.wsgi
class MainHandler(tornado.web.RequestHandler):
def get(self):
self.write("Hello, world")
tornado_app = tornado.web.Application([
(r"/", MainHandler),
])
application = tornado.wsgi.WSGIAdapter(tornado_app)
~~~
See the [appengine example application](https://github.com/tornadoweb/tornado/tree/stable/demos/appengine) [https://github.com/tornadoweb/tornado/tree/stable/demos/appengine] for afull-featured AppEngine app built on Tornado.
© Copyright 2009-2016, The Tornado Authors. Created using [Sphinx](http://sphinx-doc.org/) 1.3.5.
- User's guide
- Introduction
- Asynchronous and non-Blocking I/O
- Coroutines
- Queue example - a concurrent web spider
- Structure of a Tornado web application
- Templates and UI
- Authentication and security
- Running and deploying
- Web framework
- tornado.web — RequestHandler and Application classes
- tornado.template — Flexible output generation
- tornado.escape — Escaping and string manipulation
- tornado.locale — Internationalization support
- tornado.websocket — Bidirectional communication to the browser
- HTTP servers and clients
- tornado.httpserver — Non-blocking HTTP server
- tornado.httpclient — Asynchronous HTTP client
- tornado.httputil — Manipulate HTTP headers and URLs
- tornado.http1connection – HTTP/1.x client/server implementation
- Asynchronous networking
- tornado.ioloop — Main event loop
- tornado.iostream — Convenient wrappers for non-blocking sockets
- tornado.netutil — Miscellaneous network utilities
- tornado.tcpclient — IOStream connection factory
- tornado.tcpserver — Basic IOStream-based TCP server
- Coroutines and concurrency
- tornado.gen — Simplify asynchronous code
- tornado.concurrent — Work with threads and futures
- tornado.locks – Synchronization primitives
- tornado.queues – Queues for coroutines
- tornado.process — Utilities for multiple processes
- Integration with other services
- tornado.auth — Third-party login with OpenID and OAuth
- tornado.wsgi — Interoperability with other Python frameworks and servers
- tornado.platform.asyncio — Bridge between asyncio and Tornado
- tornado.platform.caresresolver — Asynchronous DNS Resolver using C-Ares
- tornado.platform.twisted — Bridges between Twisted and Tornado
- Utilities
- tornado.autoreload — Automatically detect code changes in development
- tornado.log — Logging support
- tornado.options — Command-line parsing
- tornado.stack_context — Exception handling across asynchronous callbacks
- tornado.testing — Unit testing support for asynchronous code
- tornado.util — General-purpose utilities
- Frequently Asked Questions
- Release notes
- What's new in Tornado 4.3
- What's new in Tornado 4.2.1
- What's new in Tornado 4.2
- What's new in Tornado 4.1
- What's new in Tornado 4.0.2
- What's new in Tornado 4.0.1
- What's new in Tornado 4.0
- What's new in Tornado 3.2.2
- What's new in Tornado 3.2.1
- What's new in Tornado 3.2
- What's new in Tornado 3.1.1
- What's new in Tornado 3.1
- What's new in Tornado 3.0.2
- What's new in Tornado 3.0.1
- What's new in Tornado 3.0
- What's new in Tornado 2.4.1
- What's new in Tornado 2.4
- What's new in Tornado 2.3
- What's new in Tornado 2.2.1
- What's new in Tornado 2.2
- What's new in Tornado 2.1.1
- What's new in Tornado 2.1
- What's new in Tornado 2.0
- What's new in Tornado 1.2.1
- What's new in Tornado 1.2
- What's new in Tornado 1.1.1
- What's new in Tornado 1.1
- What's new in Tornado 1.0.1
- What's new in Tornado 1.0