请查看 demo 中的 appengine
范例,它是一个基于 Tornado 的完整的 AppEngine 应用。
注意事项和社区支持
因为 FriendFeed 以及其他 Tornado 的主要用户在使用时都是基于 nginx或者 Apache 代理之后的。所以现在 Tornado 的 HTTP 服务部分并不完整,它无法处理多行的 header 信息,同时对于一 些非标准的输入也无能为力。
你可以在 Tornado 开发者邮件列表 中讨论和提交 bug。
发布时间:2014-07-25 17:42:00编辑:songlin阅读(285)
一个 Web 应用的性能表现,主要看它的整体架构,而不仅仅是前端的表现。 和其它的 Python Web 框架相比,Tornado 的速度要快很多。
我们在一些流行的 Python Web 框架上(Django、 web.py、CherryPy), 针对最简单的 Hello, world 例子作了一个测试。对于 Django 和 web.py,我们使用 Apache/mod_wsgi 的方式来带,CherryPy 就让它自己裸跑。这也是在生产环境中各框架常用 的部署方案。对于我们的 Tornado,使用的部署方案为前端使用 nginx 做反向代理,带动 4 个线程模式的 Tornado,这种方案也是我们推荐的在生产环境下的 Tornado 部署方案(根据具体的硬件情况,我们推荐一个 CPU 核对应一个 Tornado 伺服实例, 我们的负载测试使用的是四核处理器)。
我们使用 Apache Benchmark (ab
),在另外一台机器上使用了如下指令进行负载测试:
- ab -n 100000 -c 25 http://10.0.1.x/
在我们的测试当中,相较于第二快的服务器,Tornado 在数据上的表现也是它的 4 倍之 多。即使只用了一个 CPU 核的裸跑模式,Tornado 也有 33% 的优势。
这个测试不见得非常科学,不过从大体上你可以看出,我们开发 Tornado 时对于性能 的注重程度。和其他的 Python Web 开发框架相比,它不会为你带来多少延时。
在 FriendFeed 中,我们使用 nginx 做负载均衡和静态文件伺服。 我们在多台服务器上,同时部署了多个 Tornado 实例,通常,一个 CPU 内核 会对应一个 Tornado 线程。
因为我们的 Web 服务器是跑在负载均衡服务器(如 nginx)后面的,所以需要把 xheaders=True
传到 HTTPServer
的构造器当中去。这是为了让 Tornado 使用 X-Real-IP
这样的的 header 信息来获取用户的真实 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 false;
- proxy_set_header X-Real-IP $remote_addr;
- proxy_set_header X-Scheme $scheme;
- proxy_pass http://frontends;
- }
- }
- }
Tornado 对 WSGI 只提供了有限的支持,即使如此,因为 WSGI 并不支持非阻塞式的请求,所以如果你使用 WSGI 代替 Tornado 自己的 HTTP 服务的话,那么你将无法使用 Tornado 的异步非阻塞式的请求处理方式。 比如@tornado.web.asynchronous
、httpclient
模块、auth
模块, 这些将都无法使用。
你可以通过 wsgi
模块中的 WSGIApplication
创建一个有效的 WSGI 应用(区别于 我们用过的tornado.web.Application
)。下面的例子展示了使用内置的 WSGI CGIHandler
来创建一个有效的 Google AppEngine 应用。
- import tornado.web
- import tornado.wsgi
- import wsgiref.handlers
- class MainHandler(tornado.web.RequestHandler):
- def get(self):
- self.write("Hello, world")
- if __name__ == "__main__":
- application = tornado.wsgi.WSGIApplication([
- (r"/", MainHandler),
- ])
- wsgiref.handlers.CGIHandler().run(application)
请查看 demo 中的 appengine
范例,它是一个基于 Tornado 的完整的 AppEngine 应用。
因为 FriendFeed 以及其他 Tornado 的主要用户在使用时都是基于 nginx或者 Apache 代理之后的。所以现在 Tornado 的 HTTP 服务部分并不完整,它无法处理多行的 header 信息,同时对于一 些非标准的输入也无能为力。
你可以在 Tornado 开发者邮件列表 中讨论和提交 bug。
本文来源地址:http://sebug.net/paper/books/tornado/