tornado是有它自己的HTTP服务器的
一般情况
def main():
app = make_app()
app.listen(8888)
IOLoop.current().start()
if __name__ == '__main__':
main()
需要 改变可以同时打开的文件数量,通过/etc/security/limits.conf或者supvisor的minfds设置。
启动多个进程
def main():
app = make_app()
server = tornado.httpserver.HTTPServer(app)
server.bind(8888)
server.start(0) # 有几个CPU启动几个 共享一个端口
IOLoop.current().start()
限制问题是每个进程是有自己的IOloop,不能动全局的IOloop;无法做不停机更新;因为都监听一个端口没法有效控制。
最好是分开启动它们,每个监听不同的端口,可以使用supvisor的process groups概念。当每个进程监听不同端口时,可以使用负载均衡例如HAProxy或nginx来分发。
在负载均衡之后
可以在初始化HTTPServer时传入xheaders=True来告诉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;
}
}
}
静态文件部署
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开头的访问都会到static dir中去。包括robots.txt和favicon.ico。
FriendFeed - {{ _("Home") }}
![]({{ static_url()
使用static_url的url是/static/images/logo.png?v=aae54会包含一个该文件的hash值,这样在图片未改变之前都会cache。
使用nginx来提供静态文件:
location /static/ {
root /var/friendfeed/static;
if ($query_string) {
expires max;
}
}
debug和自动重载
启用了debug后
autoreload=True 监听文件变化并自动reload 使用了该设置则不能用多进程
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 is not caught, an error page including a stack trace will be generated.
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)