利用 NGINX 最大化 Python 性能,第一部分:Web 服务和缓存
tdxxfox
8年前
<h2>简介---如何在Python 中使用 NGINX</h2> <p>Python 的著名之处在于使用简单方便,软件开发简单,而且据说运行性能优于其它脚本语言。(虽然最新版本的 PHP、PHP 7 可能会与它展开激烈竞争。)</p> <p>所有人都希望自己的网站和应用程序运行得更快一些。但是,每个网站在流量增长或骤然出现流量峰值时都很容易发生性能问题、甚至宕机(这一般会在服务器最繁忙的时候发生)。此外在运行期间,无论是流量稳步增长还是使用量急速飞涨,几乎所有的网站都会经历性能问题。</p> <p>而关于网站性能问题,那就到了 NGINX 和 NGINX Plus 发挥作用的时候。一般可以通过以下三种方式改善网站性能:</p> <p>作 web 服务器,NGINX 最开始的设计初衷就是解决 <a href="/misc/goto?guid=4959636191242740994" rel="nofollow,noindex">C10K 问题</a> ,也就是能够轻松支持1万以上的同时访问。NGINX 作为 Python web 服务器,可以保证网站在流量较低时依然能够快速运行。当拥有上千名用户同时访问时,你就必须提供更高性能、更少崩溃和更短停机时间的 web 服务器。你也可以在 NGINX 服务器上进行静态文件缓存或微程序缓存,不过这两者在单独的 NGINX 反向代理服务器(见下段)上运行表现更佳。</p> <p>做反向代理服务器---你可以在当前应用程序服务器上把 NGINX 设置为反向代理服务器。NGINX 会将网络请求发送到应用程序服务器。这种 古怪招数 可以提高网站运行速度,减少停机时间,减少服务器资源占用,并且提高网站安全。你还可以在反向代理服务器上缓存静态数据(非常高效),增加动态数据的微程序缓存,从而减少应用程序本身的负担。</p> <p>做多个应用程序服务器的负载均衡器---首先要部署反向代理服务器,然后同时运行多个应用程序服务器,并使用 NGINX 或 NGINX Plus 来均衡经过它们传送的流量,进行扩展。通过这种部署,你可以轻松的根据流量需求来衡量网站性能,增加可靠度和正常运行时间。如果你需要既定的用户会话保存在同一个服务器上,配置负载均衡器还可以支持会话持久性。</p> <p>无论是将其用作 Python 的 web 服务器、反向代理服务器、负载均衡器,还是同时使用以上三种功能,NGINX 和 NGINX Plus 都能带来很大好处。</p> <p>下面介绍改善 Python 应用程序性能的5个技巧,本文会分多个章节进行介绍,包括把 NGINX 和 NGINX Plus 作为 web 服务器,如何实施静态文件缓存,以及微程序缓存应用程序生成文件。</p> <p>再就是下篇文章会介绍如何把 NGINX 和 NGINX Plus 当作反向代理服务器和多个应用程序服务器的负载均衡器。</p> <h2>技巧1---找出 Python 性能瓶颈</h2> <p>检验 Python 应用程序的性能分有两种不同情况。</p> <p>第一种是在日常 合理 用户数量的情况下;第二种则是负载很大的情况下。</p> <p>很多站长毫不关心轻负载时网站性能,然而残酷的现实经验告诉我们,响应时间的每一秒钟都应该为之捏一把汗。虽然将响应时间缩短几毫秒是个苦差事,但却会带来更好的用户体验和更好的运营成果。</p> <p>另一个就是所有人都担心的情境:网站繁忙时出现的性能问题,例如运行严重减缓和崩溃。此外,很多黑客攻击也会表现为用户数量急剧增长,所以改善网站性能也是解决攻击问题的重要一步。</p> <p>服务器会为每位用户分配一定数量的内存,例如 Apache HTTP 服务器,随着用户数量的增长,物理内存就会出现超负载,服务器开始将数据交换到磁盘上,性能骤降,随之产生性能不良和崩溃。而使用 NGINX 将会有助于解决这一问题。</p> <p>Python 尤其容易发生内存相关的性能问题,因为它在执行任务时一般比其它脚本语言占用更多的内存(因此它的执行速度更高)。因此,在同等条件下,基于 Python 的应用程序可能比其他语言的应用程序能承受的用户负载量更小。</p> <p>优化应用程序或多或少会有帮助,但是这通常不是流量相关网站性能问题的最佳或最快解决方案。本文后续会列出解决流量相关问题的最佳/最快解决方案。当你看完本文后,无论如何都要回去优化你的应用程序,或者使用微服务架构重写。</p> <h2>技巧2---选择单一服务器或多个服务器配置</h2> <p>小网站只要配置单一服务器就可以运行良好,大网站则需要有多个服务器。而如果你处于中间地带,或者你的网站正在由小网站发展壮大起来,那你就要做些有意思的选择了。</p> <p>如果你只具配置有单一服务器的能力,那么在流量峰值或整体流量迅速增长时,你会面临极大风险。因为此时你的扩展性会受限,此时解决问题的方法可以包括优化应用程序、把网络服务器改成 NGINX、换一个更大更快的服务器,或者把存储任务转移到内容分发网络(CDN)等。这其中每一个选项都需要耗费时间、成本,并且在实施过程中都可能会引入错误和问题。</p> <p>此外,配置单一服务器的情况下,你的网站就有一个单一的失败节点,会有很多问题造成你的网站下线而且没有很快很简捷的解决方法。</p> <p><img src="https://simg.open-open.com/show/0e4463fb485a8dc0887a243214b3df09.png"></p> <p>如果把配置单一服务器改成使用 NGINX,可以自由选择开源 NGINX 软件或 NGINX Plus。NGINX Plus 包含企业级支持及额外功能。有些额外功能可以在单一服务器配置状况下实现,例如实时活动监控,而有些功能只有在把 NGINX Plus 作为多个服务器配置下的反向代理服务器时才会出现,例如负载均衡和会话持久性。</p> <p>综合以上所有考虑,除非你确信你的网站在接下来很长时间都不会扩大规模,停止运行也不会有大麻烦,可以抵抗一些其他的风险,那可以配置单一服务器。但别忘了配置多个服务器几乎可以随意扩展---单次失败完全可以处理,能迅速扩充容量,总之你应用的性能由你来定。Tip 3 – Change Your Web Server to NGINX</p> <h2>技巧3---把你的 web 服务器改成 NGINX</h2> <p>在网络发展早期, Apache 等同于 web 服务器 。但是21世纪初 NGINX 诞生之后使用越来越广泛,它已经成为世界上流量前1000、10000和100000的公司的 web 服务器首选。</p> <p>NGINX 的设计初衷是解决 <a href="/misc/goto?guid=4959636191242740994" rel="nofollow,noindex">C10K 问题</a> ,也就是在给定内存预算的范围内能支持10000以上的同时访问。其它 web 服务器需要给每个访问分配一部分内存,但当几千名用户同时访问网站时,它们会面临物理内存不足、运行缓慢或崩溃的问题。NGINX 分别处理每个请求,并且能优雅地应对更多用户。(如上文所述,它在其它功能方面也有优异表现。)</p> <p>以下为 NGINX 架构的概述。</p> <p><img src="https://simg.open-open.com/show/1ef469dbc864767122e07d17de24d87b.png"></p> <p>在本图中,是一个 Python 应用程序服务器的后台应用程序块,需要通过 FastCGI 来访问。NGINX 并不 知道 如何运行 Python,因此它需要关口来进入一个能够运行 Python 的环境。FastCGI 是 PHP、Python 等语言广泛使用的用户界面。</p> <p>不过,Python 和 NGINX 之间的沟通更常用的备选是服务网关接口(WSGI)。WSGI 可以在多线程、多进程环境下运行,因此它可以在本文提到的所有配置选择下运行。</p> <p>如果你要把 NGINX 作成你的 web 服务器,网上有很多操作帮助信息:</p> <ul> <li> <p>配置 gunicorn--- 绿色独角兽 ,一个很有名的 WSGI 服务器,以便使用 NGINX。</p> </li> <li> <p>配置 uWSGI---另外一个有名的 WSGI 服务器,以便使用 NGINX。uWSGI 能够直接支持 NGINX.</p> </li> <li> <p>使用 uWSGI、NGINX 和Django---一个很常用的 Python 网站框架。</p> </li> </ul> <p>以下片段展示了如何配置 NGINX 来与 uWSGI 一起运行(给出的实例是使用 Python 框架 Django 的一个项目):</p> <pre> <code class="language-nginx">http { ... upstream django { server 127.0.0.1:29000; } server { listen 80; server_name myapp.example.com; root /var/www/myapp/html; location / { index index.html; } location /static/ { alias /var/django/projects/myapp/static/; } location /main { include /etc/nginx/uwsgi_params; uwsgi_pass django; uwsgi_param Host $host; uwsgi_param X-Real-IP $remote_addr; uwsgi_param X-Forwarded-For $proxy_add_x_forwarded_for; uwsgi_param X-Forwarded-Proto $http_x_forwarded_proto; } } }</code></pre> <h2>技巧4——实施静态数据缓存</h2> <p>静态数据缓存包括把那些不经常变更的文件(也许是几个小时,也许是永远不变)存在一个应用程序服务器之外的地方。静态文件的典型例子就是作为网页内容一部分展示的 JPEG 图像。</p> <p>静态文件缓存是增强应用程序性能的常见方法,而且实际上会在下面几个层面发生:</p> <ul> <li> <p>在用户的浏览器</p> </li> <li> <p>多个层级的网络提供者---从一家公司的内网,到互联网服务供应商(ISP)</p> </li> <li> <p>web 服务器上,正如本文所述</p> </li> </ul> <p>在 web 服务器上实施静态数据缓存有两个好处:</p> <ul> <li> <p>更快传送到用户---NGINX 针对静态数据缓存做了优化,执行静态内容的请求的速度比应用程序服务器快得多。</p> </li> <li> <p>减轻应用服务器的负担---应用服务器甚至不会收到缓存静态数据的请求,因为网络服务器已经实现这些请求。</p> </li> </ul> <p>静态文件缓存在单个服务器执行时运行良好,但其实硬件方面依然由 web 服务器和应用程序服务器共享。如果 web 服务器使用硬件找回缓存的文件(及时非常高效)应用程序将无法使用这些硬件,运行速度可能会受到影响。</p> <p>要支持浏览器缓存,就要为静态文件正确设置 HTTP 标头。需要考虑 HTTP 缓存控制标头(尤其是它的 max‑age 设置)、Expires 标头和实体标签。如想了解更多这方面的介绍,可以参考 <a href="/misc/goto?guid=4959672284085570513" rel="nofollow,noindex">Using NGINX as an Application Gateway with uWSGI and Django</a> 。</p> <p>下面的代码配置 NGINX,用于缓存包括 JPEG 文件、GIF、PNG文件、MP4 视频文件、ppt等多种类型的静态文件(用你的网址来替代 <a href="/misc/goto?guid=4958965121181731048" rel="nofollow,noindex">www.example.com</a> ):</p> <pre> <code class="language-nginx">server { # substitute your web server's URL for "www.example.com" server_name www.example.com; root /var/www/example.com/htdocs; index index.php; access_log /var/log/nginx/example.com.access.log; error_log /var/log/nginx/example.com.error.log; location / { try_files $uri $uri/ /index.php?$args; } location ~ \.php$ { try_files $uri =404; include fastcgi_params; # substitute the socket, or address and port, of your Python server fastcgi_pass unix:/var/run/php5-fpm.sock; #fastcgi_pass 127.0.0.1:9000; } location ~* .(ogg|ogv|svg|svgz|eot|otf|woff|mp4|ttf|css|rss|atom|js|jpg |jpeg|gif|png|ico|zip|tgz|gz|rar|bz2|doc|xls|exe|ppt|tar|mid |midi|wav|bmp|rtf)$ { expires max; log_not_found off; access_log off; } }</code></pre> <h2>技巧5---实施微程序缓存</h2> <p>微程序缓存给了那些运行 Python、PHP 和其它语言的应用程序服务器提升性能的大好机会。可缓存的网页有以下3种类型:</p> <ul> <li> <p>静态文件---可缓存,见技巧4。</p> </li> <li> <p>应用程序生成的、非个人化页面---一般来说,缓存这些没有意义,因为这些内容需要不断更新。举个例子,用户登录电商网站时看到的页面(见下段)---在售商品、同类商品推荐等等可能会不断更新,因此提供最新的网页非常重要。不过,如果另一个用户在十分之一秒之后登录,向他们展示前一位用户看到的同样页面也没有什么问题。</p> </li> <li> <p>应用程序生成的、个人化页面---这些通常不会被缓存,因为它们是单个用户所属,而且同一个用户也不会再次看到相同的页面。举个例子,用户登录电商网站后看到的页面,同样的页面不能向其他用户展示。</p> </li> </ul> <p><img src="https://simg.open-open.com/show/f191b1f4c93ec8bbe426a5b9f28ac7e5.png"></p> <p>微程序缓存对上述第二种网页类型很有用---应用程序生成的、非个人化页面。 微 是一个概括的时间范围。如果你的网站一秒之内数次生成同样的页面,缓存一秒钟就不会影响网页的新鲜度。不过这个概括的缓存时间期限会极大地减轻应用程序服务器的负担,尤其是流程峰值期间。在缓存逾时期限内,不用生成10次、20次、100次(相同的内容),而是生成既定的网页一次,然后这个网页就会被缓存,并且通过缓存向多个用户展示。</p> <p>而这产生的效果却不可思议,服务器一秒钟处理大量请求运行缓慢,但在只处理一个请求时速度是极快的。(当然,还有任何个人化的页面)。设置一秒超时的代理服务器这样的核心变化只需要几行配置代码。</p> <pre> <code class="language-nginx">proxy_cache_path /tmp/cache keys_zon\=cache:10m levels=1:2 inactive=600s max_size=100m; server { proxy_cache cache; proxy_cache_valid 200 1s; ... }</code></pre> <p>更多配置范例,请参考 Tyler Hicks‑Wright 的博客 <a href="/misc/goto?guid=4959672284199829312" rel="nofollow,noindex">Python and uWSGI with NGINX.</a> 。</p> <h2>结论</h2> <p>在这部分,本文介绍了提升单一服务器提升 Python 应用性能的解决方案,可以在单一服务器上配置,也可在反向代理服务器或单独的缓存服务器上实现。(缓存在单独的服务器上运行更好。)接下来的第二部分介绍需要两个或更多服务器才能实现的性能提升方案。</p> <p>当然如果你想了解 <a href="/misc/goto?guid=4959672284282799007" rel="nofollow,noindex">NGINX Plus</a> 适用于你的应用程序的高级功能,例如支持、实时活动监控、运行时同时配置,可以去 <a href="/misc/goto?guid=4959672284282799007" rel="nofollow,noindex">NGINX</a> 官网查看。</p> <p>来自: http://blog.oneapm.com/oneapm-course/677.htm</p>