Running and deploying

由于Tornado提供了自己的HTTPServer,因此运行和部署它与其他Python Web框架略有不同. 无需配置WSGI容器来查找您的应用程序,而是编写一个main()函数来启动服务器:

def main():
    app = make_app()
    app.listen(8888)
    IOLoop.current().start()

if __name__ == '__main__':
    main()

配置您的操作系统或进程管理器以运行此程序来启动服务器. 请注意,可能有必要增加每个进程的打开文件数(以避免"打开的文件太多"-错误). 要提高此限制(例如将其设置为50000),可以使用ulimit命令,修改/etc/security/limits.conf或在受监管的配置中设置minfds .

Processes and ports

由于Python GIL(全局解释器锁定),必须运行多个Python进程才能充分利用多CPU机器. 通常,最好每个CPU运行一个进程.

龙卷风包含一个内置的多进程模式,可以一次启动多个进程. 这需要对标准主要功能进行一些改动:

def main():
    app = make_app()
    server = tornado.httpserver.HTTPServer(app)
    server.bind(8888)
    server.start(0)  # forks one process per cpu
    IOLoop.current().start()

这是启动多个进程并使它们全部共享同一端口的最简单方法,尽管它有一些限制. 首先,每个子进程都有其自己的IOLoop ,因此在派生之前,什么都不能碰到全局IOLoop实例(甚至是间接地),这一点很重要. 其次,在此模型中很难进行零停机时间更新. 最后,由于所有进程共享同一个端口,因此更难单独监视它们.

对于更复杂的部署,建议独立启动进程,并让每个进程在不同的端口上侦听. 主管的"流程组"功能是安排此操作的一种好方法. 当每个进程使用不同的端口时,通常需要外部负载平衡器(例如HAProxy或nginx)向外部访问者提供一个地址.

Running behind a load balancer

在像nginx这样的负载均衡器后面运行时,建议将xheaders=True传递给HTTPServer构造函数. 这将告诉Tornado使用X-Real-IP类的标头来获取用户的IP地址,而不是将所有流量都分配给平衡器的IP地址.

这是一个准系统的nginx配置文件,其结构与我们在FriendFeed上使用的配置类似. 假定nginx和Tornado服务器在同一台计算机上运行,​​并且四个Tornado服务器在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

您可以通过在应用程序中指定static_path设置来从Tornado提供静态文件:

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)

此设置将自动使所有以/static/开头的请求从该静态目录中服务,例如http://localhost:8888/static/foo.png将为指定的静态目录中的文件foo.png服务. 我们还会从静态目录自动提供/robots.txt/favicon.ico (即使它们不是以/static/前缀开头).

在上面的设置中,我们已经明确配置了Tornado以使用StaticFileHandler从根目录提供apple-touch-icon.png ,尽管它实际上位于静态文件目录中. (该正则表达式中的捕获组对于告诉StaticFileHandler请求的文件名是必需的;请记住,捕获组作为方法参数传递给处理程序.)您可以执行相同的操作,例如从站点根目录提供sitemap.xml . 当然,也可以通过在HTML中使用适当的<link />标记来避免伪造root apple-touch-icon.png .

为了提高性能,对于浏览器来说,积极地缓存静态资源通常是一个好主意,这样浏览器就不会发送不必要的If-Modified-SinceEtag请求,而这些请求可能会阻止页面的呈现. 龙卷风支持静态内容版本控制 .

要使用此功能,请在模板中使用static_url方法,而不是直接在HTML中键入静态文件的URL:

<html>
   <head>
      <title>FriendFeed - {{ _("Home") }}</title>
   </head>
   <body>
     <div><img src="{{ static_url("images/logo.png") }}"/></div>
   </body>
 </html>

static_url()函数会将相对路径转换为看起来像/static/images/logo.png?v=aae54的URI. v参数是logo.png内容的哈希,并且它的存在使Tornado服务器将缓存头发送到用户的浏览器,这将使浏览器无限期地缓存内容.

由于v参数基于文件的内容,因此,如果您更新文件并重新启动服务器,它将开始发送新的v值,因此用户的浏览器将自动获取新文件. 如果文件内容没有更改,浏览器将继续使用本地缓存的副本,而无需检查服务器上的更新,从而显着提高了渲染性能.

在生产中,您可能希望从更优化的静态文件服务器(如nginx)提供静态文件. 您可以配置几乎所有Web服务器以识别static_url()使用的版本标签,并相应地设置缓存头. 这是我们在FriendFeed中使用的nginx配置的相关部分:

location /static/ {
    root /var/friendfeed/static;
    if ($query_string) {
        expires max;
    }
 }

Debug mode and automatic reloading

如果将debug=True传递给Application构造函数,则该应用将以调试/开发模式运行. 在此模式下,将启用一些在开发过程中为方便起见而使用的功能(每个功能也可以作为单独的标志使用;如果同时指定了单独的标志,则优先使用):

  • autoreload=True: The app will watch for changes to its source files and reload itself when anything changes. This reduces the need to manually restart the server during development. However, certain failures (such as syntax errors at import time) can still take the server down in a way that debug mode cannot currently recover from.

  • compiled_template_cache=False :将不会缓存模板.

  • static_hash_cache=False :不会缓存静态文件哈希(由static_url函数使用).

  • serve_traceback=True :当未捕获RequestHandler的异常时,将生成包含堆栈跟踪的错误页面.

自动重载模式与HTTPServer的多进程模式不兼容. 如果您使用的是自动重载模式,则不得为HTTPServer.start一个非1的参数(或调用tornado.process.fork_processes ).

调试模式的自动重新加载功能作为tornado.autoreload的独立模块tornado.autoreload . 可以将两者结合使用以提供针对语法错误的额外鲁棒性:在应用程序内设置autoreload=True在运行时检测更改,并使用python -m tornado.autoreload myserver.py启动以捕获任何语法错误或其他错误.启动时出错.

重新加载会丢失任何Python解释器命令行参数(例如-u ),因为它使用sys.executablesys.argv重新执行Python. 此外,修改这些变量将导致重新加载行为不正确.

在某些平台上(包括10.6之前的Windows和Mac OSX),该过程无法"就地"更新,因此,当检测到代码更改时,旧服务器将退出,而新服务器将启动. 众所周知,这会混淆某些IDE.