网站首页学无止境PYTHON

python框架Tornado之三性能与环境

发布时间:2014-07-25 17:42:00编辑:songlin阅读(191)

    性能

    一个 Web 应用的性能表现,主要看它的整体架构,而不仅仅是前端的表现。 和其它的 Python Web 框架相比,Tornado 的速度要快很多。

    我们在一些流行的 Python Web 框架上(Django、 web.pyCherryPy), 针对最简单的 Hello, world 例子作了一个测试。对于 Django 和 web.py,我们使用 Apache/mod_wsgi 的方式来带,CherryPy 就让它自己裸跑。这也是在生产环境中各框架常用 的部署方案。对于我们的 Tornado,使用的部署方案为前端使用 nginx 做反向代理,带动 4 个线程模式的 Tornado,这种方案也是我们推荐的在生产环境下的 Tornado 部署方案(根据具体的硬件情况,我们推荐一个 CPU 核对应一个 Tornado 伺服实例, 我们的负载测试使用的是四核处理器)。

    我们使用 Apache Benchmark (ab),在另外一台机器上使用了如下指令进行负载测试:

    1. 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 端口上:
     

    1. user nginx; 
    2. worker_processes 1
    3.  
    4. error_log /var/log/nginx/error.log; 
    5. pid /var/run/nginx.pid; 
    6.  
    7. events { 
    8.     worker_connections 1024
    9.     use epoll; 
    10.  
    11. http { 
    12.     # Enumerate all the Tornado servers here
    13.     upstream frontends { 
    14.         server 127.0.0.1:8000
    15.         server 127.0.0.1:8001
    16.         server 127.0.0.1:8002
    17.         server 127.0.0.1:8003
    18.     } 
    19.  
    20.     include /etc/nginx/mime.types; 
    21.     default_type application/octet-stream; 
    22.  
    23.     access_log /var/log/nginx/access.log; 
    24.  
    25.     keepalive_timeout 65
    26.     proxy_read_timeout 200
    27.     sendfile on; 
    28.     tcp_nopush on; 
    29.     tcp_nodelay on; 
    30.     gzip on; 
    31.     gzip_min_length 1000
    32.     gzip_proxied any; 
    33.     gzip_types text/plain text/html text/css text/xml 
    34.                application/x-javascript application/xml 
    35.                application/atom+xml text/javascript; 
    36.  
    37.     # Only retry if there was a communication error, not a timeout
    38.     # on the Tornado server (to avoid propagating "queries of death"
    39.     # to all frontends)
    40.     proxy_next_upstream error; 
    41.  
    42.     server { 
    43.         listen 80
    44.  
    45.         # Allow file uploads
    46.         client_max_body_size 50M
    47.  
    48.         location ^~ /static/ { 
    49.             root /var/www; 
    50.             if ($query_string) { 
    51.                 expires max; 
    52.             } 
    53.         } 
    54.         location = /favicon.ico { 
    55.             rewrite (.*) /static/favicon.ico; 
    56.         } 
    57.         location = /robots.txt { 
    58.             rewrite (.*) /static/robots.txt; 
    59.         } 
    60.  
    61.         location / { 
    62.             proxy_pass_header Server; 
    63.             proxy_set_header Host $http_host; 
    64.             proxy_redirect false; 
    65.             proxy_set_header X-Real-IP $remote_addr; 
    66.             proxy_set_header X-Scheme $scheme; 
    67.             proxy_pass http://frontends; 
    68.         } 
    69.     } 

    WSGI 和 Google AppEngine

    Tornado 对 WSGI 只提供了有限的支持,即使如此,因为 WSGI 并不支持非阻塞式的请求,所以如果你使用 WSGI 代替 Tornado 自己的 HTTP 服务的话,那么你将无法使用 Tornado 的异步非阻塞式的请求处理方式。 比如@tornado.web.asynchronoushttpclient 模块、auth 模块, 这些将都无法使用。

    你可以通过 wsgi 模块中的 WSGIApplication 创建一个有效的 WSGI 应用(区别于 我们用过的tornado.web.Application)。下面的例子展示了使用内置的 WSGI CGIHandler 来创建一个有效的 Google AppEngine 应用。

    1. import tornado.web 
    2. import tornado.wsgi 
    3. import wsgiref.handlers 
    4.  
    5. class MainHandler(tornado.web.RequestHandler): 
    6.     def get(self): 
    7.         self.write("Hello, world"
    8.  
    9. if __name__ == "__main__"
    10.     application = tornado.wsgi.WSGIApplication([ 
    11.         (r"/", MainHandler), 
    12.     ]) 
    13.     wsgiref.handlers.CGIHandler().run(application) 

    请查看 demo 中的 appengine 范例,它是一个基于 Tornado 的完整的 AppEngine 应用。

    注意事项和社区支持

    因为 FriendFeed 以及其他 Tornado 的主要用户在使用时都是基于 nginx或者 Apache 代理之后的。所以现在 Tornado 的 HTTP 服务部分并不完整,它无法处理多行的 header 信息,同时对于一 些非标准的输入也无能为力。

    你可以在 Tornado 开发者邮件列表 中讨论和提交 bug。


本文来源地址:http://sebug.net/paper/books/tornado/