Running and deploying
tornado 有自己的 HTTPServer,他的运行和部署和其他的 python web 框架略有不同。
你只需要写一个 main() 函数启动服务,不需要配置 WSGI 容器来找到你的应用。
def main():
app = make_app()
app.listen(8888)
IOLoop.current().start()
if __name__ == '__main__':
main()
配置你的操作系统或者进程管理器来运行程序启动服务。增加每个进程打开文件数量是很有必要的 (避免 Too many open files 错误)。
通过 ulimit 命令行设置 (一般为 50000)。修改 /etc/security/limits.conf 或者设置管理的 minfds 配置。
Processes and ports
由于 python 的 GIL(Global Interpreter Lock),需要运行多个 python 进程才能充分利用机器的 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 实例。
第二:很难做到零宕机下更新模块
第三:因为所有进程共享相同的端口,所以跟男去分别监控他们。
对于复杂的部署,建议使用独立的进程,每一个监听不同的端口。进程组管理特征是有一个很好的方式去实现。
当每一个进程使用不同的端口时,一个外部负载均衡器如 HAProxy 或者 nginx 通常需要呈现一个唯一地址给外部访问。
Running behind a load balancer
当运行在负载均衡器如 nginx 时,建议在 HTTPServer 构造器中传递 xheaders=True。他将会告诉 tornado 使用 X-Real-IP 获取用户的 IP 地址,而不是将所有流量传给平衡器的 IP 地址。
这是一个简单的 nginx 配置,在结构上类似于我们使用的 FriendFeed。他假定 nginx 和 tornado 服务运行在同一台机器,四个 tornado 服务运行在 8000-8003 4个端口。
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
在 tornado 中,可以通过 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)
这个设置会自动将所有以 /static/ 开头的请求到指定的静态文件目录。我们也会自动的将 /robots.txt 和 /favicon.ico 服务到指定的静态文件。(即使他们不是以 /static/ 开头)
上文中,尽管在静态文件夹中,我们还是明确的使用 StaticFileHandler 配置 tornado 服务的 apple-touch-icon.png 文件。
为了提高性能,浏览器通常会积极缓存静态资源,这样浏览器可以避免发送不必要可能阻塞页面呈现的 If-Modified-Since 或者 Etag 请求。
tornado 支持开箱即用的静态资源版本管理。
为了使用这个特性,在模板中使用 static_url 方法而不是直接将静态文件的 URL 输入在 HTML 中。
<html>
<head>
<title>FriendFeed - {{ _("Home") }}</title>
</head>
<body>
<div><img src="{{ static_url("images/logo.png") }}"/></div>
</body>
</html>
static_url() 函数将相对路径转化为 URI 地址,如:/static/images/logo.png?v=aae54 。参数 v 是 logo.png 值的散列值,他是 tornado 服务通过发送缓存头告知用户浏览器去做无限期的缓存。
因为 v 参数是基于文件内容的,如果你更新了文件并且重启了服务,他将会发送一个新的 v 值,这样用户的浏览器就可以自动获取新文件。如果文件的内容没有发生改变,浏览器会继续使用本地的缓存,不再更新服务上的文件。显著提高渲染性能。
在生产中你可能希望为静态文件提供一个更优的静态文件服务器,如 nginx。
你可以配置任何服务器去识别 static_url() 所使用的版本号,并且设置相应的缓存头。如下是我们在 FriendFeed 中使用的 nginx 配置。
location /static/ {
root /var/friendfeed/static;
if ($query_string) {
expires max;
}
}
Debug mode and automatic reloading
如果将 debug=True 传递给 Application 构造函数,应用将会运行在调试/开发模式。
这种模式下。一些方便用于开发的功能将开启。
应用将会监视他的源文件,一旦文件发生改变,将重新加载。这减少了在开发过程中手动重启的服务的次数。
然而,一些错误(如语法错误)也可以使服务器挂掉导致无法恢复。
- compiled_template_cache=False
Templates 不缓存
静态文件序列化不再缓存
当 RequestHandler 没有捕捉到异常时,将生成一个包含堆栈跟踪的错误页面。
Autoreload 模式不兼容多进程模式的 HTTPServer。
调试模式下的自动重载特性在 tornado.autoreload 模块中独立实现。
这两个结合使用可以提供额外的健壮性对抗语法错误:
1、设置 sutoreload=True 在运行时检测变化
2、使用 python -m tornado.autoreload myser.py 在启动时捕捉语法或其他错误。
重载会丢失 python 解释器的命令行参数,因为他使用 sys.executable 和 sys.argv 重新执行 python。除此之外,修改这些变量将导致重载行为错误。
在一些平台上(Windows 和 Mac 10.6之前),进程不能直接更新,所以当检测到代码更新的时候,旧的服务退出,然后重启一个新的。
WSGI and Google App Engine
tornado 通常自己就运行而不需要一个 WSGI 容器。然而,在某些环境中(如Google App Enging),只有 WSGI 被允许,应用不能运行在自己的服务上。在这种情况下,tornado 允许一个受限制的操作模式,不支持异步操作,但是允许一系列 tornado 功能在 WSGI-only 环境。
在 WSGI 模式下,协程, @asynchronous 装饰器, AsyncHTTPClient, auth, WebSockets 特征不被允许,
你可以使用 tornado.wsgi.WSGIAdapter 将 tornado 应用转换成 WSGI 应用。这种情况下,配置好 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)
上一篇: 2.6、User’s guide (Authentication and security)
网友评论