美文网首页python进阶tornado
Tornado入门(八)【运行和部署】

Tornado入门(八)【运行和部署】

作者: nummycode | 来源:发表于2017-09-30 15:52 被阅读121次

    运行和部署

    由于Tornado自身提供了HTTPServer, 所以它的运行和部署与其他Python Web 框架不一样。我们可以直接写一个main()方法来启动一个服务器,而不是配置WSGI容器。

    def main():
        app = make_app()
        app.listen(8888)
        IOLoop.current().start()
    
    if __name__ == '__main__':
        main()
    

    配置操作系统以启动服务器,注意可能需要提高系统中单个进程可打开的文件数。

    进程和端口

    由于Python GIL锁的限制,需要通过多线程的方式来尽可能的利用好机器的多核CPU。最好是每个CPU运行一个进程。

    Tornado内置了多进程模式,只需要在main方法中做一点点改动:

    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,所以在fork之前不要操作全局的IOLoop实例。
    • 由于所有进程共享一个端口,所以管理起来更为麻烦

    对于更为复杂的部署,推荐独立启动进程,然后每个进程监听不同的端口。supervisord提供了进程组可以组织这些进程。

    当每个进程使用不同的端口时,可以使用负载均衡的服务器,比如nginx将不同端口转发至同一端口。

    运行在负载均衡服务器后面

    当使用类似nginx的负载均衡服务时,推荐传递参数xheaders=TrueHTTPServer构造器,这样Tornado才能通过X-Real-IP等头部字段获取真是的请求来源IP而不是转发服务器的IP。

    下面是一个示例配置文件:

    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_path来支持静态文件的分发。

    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)
    

    Tornado会自动分发以/static/开头的URL, 它会在static这个目录下查找这些文件。/robots.txt/favicon.ico也会自动分发。

    在上面的配置中,我们明确指定使用StaticFileHandler来处理apple-touch-icon.png请求。而applie-touch-icon.png实际位于/static/目录下。

    为了提高性能,可以告诉浏览器缓存这些静态页面。为了使用这个功能,在模板中应该使用static_url来代替正式的URL。

    <html>
       <head>
          <title>FriendFeed - {{ _("Home") }}</title>
       </head>
       <body>
         <div>![]({{ static_url()</div>
       </body>
     </html>
    

    static_url会将相对路径转换成类似/static/images/logo.png?v=aae54这样的URL,这里的v是logo.png的哈希值。Tornado通过识别它来发送缓存头给浏览器。

    由于参数v的值是基于文件内容构建的,所以更新文件或者重启服务器都会改变它的值,然后浏览器就会自动获取最新的文件。如果文件内容没有改变,则继续使用缓存的文件。

    在生产环境中,你可能想直接通过 nginx分发静态文件。

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

    调试模式和自动重启

    如果设置debug=True,应用就会开启调试模式。在这种模式下,下面的特性会开启:

    • autoreload=True 当代码发生变化时,应用会自动重启,这样就省去了手动重启的麻烦,但是当出现语法错误的时候,重启会失败。
    • compile_template_cache=False 模板不会缓存
    • static_hash_cache=False 静态文件的哈希值不会缓存。
    • serve_traceback=TrueRequestHandler中发生异常而没有被捕获时,生成一个包含错误信息的页面。

    自动重启模式与HTTPServer的多线程模式不兼容,所以当使用自动重启模式时,调用HTTPServer或者tornado.process.fork_processes时,只能指定一个进程。

    自动重启模式也可以作为单独的模块使用,调用tornado.autoreload即可。通过组合调试模式和自动重启模式,可以实现更为稳健的应用。在应用中设置debug=True,当出现语法错误时,调用python -m tornado.autoreload myserver.py进行重启。

    重启的时候,Python编译器的命令行参数就失效了,因为使用sys.executablesys.argv来执行Python。

    WSGI

    Tornado一般不需要类似WSGI的容器就可以运行,但是有些情况下,又只能使用WSGI,这时Tornado支持有限的非异步操作。不支持的操作包括协程,@asynchronous修饰器,AsyncHTTPClientauth模块和WebSocket

    也可以调用tornado.wsgi.WSGIAdapter将Tornado应用转换为WSGI应用。

    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)
    

    相关文章

      网友评论

      • next_discover:如果不配置ngix,在xshell启动一个服务,关闭窗口,服务就断了怎么回事

      本文标题:Tornado入门(八)【运行和部署】

      本文链接:https://www.haomeiwen.com/subject/vhxnextx.html