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

运行和部署

由于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)

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏iOS 开发杂谈

Mac上更新Ruby

在项目中要适配iOS10,要使用cocoapods更新第三方库,而目前使用的ruby版本是Mac系统自带的1.8.7。所以需要对ruby进行升级。这里使用rvm...

1022
来自专栏13blog.site

git删除本地分支

远端master分支有更新需要拉取至本地,但是代码有些地方做了修改导致了小冲突,但是这些修改又是无关紧要的,于是就打算直接删除掉本地分支再重新拉取master分...

3136
来自专栏技术博文

线程,进程和并发

进程 进程是什么?进程是正在执行的程序;进程是正在计算机上执行的程序实例;进程是能分配给处理器并由处理器执行的实体。 进程一般会包括指令集和系统资源集,这里的指...

3777
来自专栏北京马哥教育

基础拾遗--【转】Linux,du、df统计的硬盘使用情况不一致问题

Linux,du、df统计的硬盘使用情况不一致问题 在运维Linux服务器时,会碰到需要查看硬盘空间的情况,这时候,通常会使用df -lh命令来检查每个挂载了文...

3026
来自专栏云飞学编程

python小伙自制模板之家查询和下载文件的脚本

通过python的requests和lxml库,完成对模板之家免费模板的查询和下载功能(保存本地)

1373
来自专栏Java工程师日常干货

玩转Redis集群(下)Redis集群操作实践Redis实现Session共享Java操作Redis与Spring整合

从上面的操作,你可以看到,当存储某一个数据的时候,会分配一个slot,而这个slot从属于某一个Master,也就是说你需要明白,数据是分布的存储在Redis集...

791
来自专栏烂笔头

Django 1.10中文文档-第一个应用Part6-静态文件

目录[-] 本教程上接Part5 。前面已经建立一个网页投票应用并且测试通过,现在主要讲述如何添加样式表和图片。 除由服务器生成的HTML文件外,网页应用一...

3037
来自专栏杨建荣的学习笔记

vi的补充学习(r4笔记第25天)

今天突然发现vi虽然用了些日子了,但是常用的一些命令之外,还是有些命令比较生疏,简单总结了一下,然后自己在vi里面编辑了一把,效果还不错。 对于大家比较熟悉且常...

33510
来自专栏hbbliyong

Pyinstaller如何将资源文件一起打包至exe中

基本原理:Pyinstaller 可以将资源文件一起bundle到exe中,当exe在运行时,会生成一个临时文件夹,程序可通过sys._MEIPASS访问临时文...

1921
来自专栏性能与架构

查看Linux系统的平均负载

有时系统响应速度很慢,很可能是CPU的负载过高了,这时就要是否有大量的进程在排队等待 特定时间间隔内运行队列中的平均进程数可以反映系统的繁忙程度,所以我们通常需...

3868

扫码关注云+社区

领取腾讯云代金券