python編寫的web項目性能優化

Django

Django是什麼?

Django 是一個開放源代碼的 Web 應用框架,由 Python 寫成。採用了 MVT 的軟件設計模式,即模型 Model,視圖 View 和模板 Template。它最初是被開發來用於管理勞倫斯出版集團旗下的一些以新聞內容爲主的網站的。並於 2005 年 7 月在 BSD 許可證下發布。

這套框架是以比利時的吉普賽爵士吉他手 Django Reinhardt 來命名的。

Django 的主要目標是使得開發複雜的、數據庫驅動的網站變得簡單。Django 注重組件的重用性和“可插拔性”,敏捷開發和 DRY 法則(Don’t Repeat Yourself)。在 Django 中 Python 被普遍使用,甚至包括配置文件和數據模型。

Django 於 2008 年 6 月 17 日正式成立基金會。

Django 框架的核心包括:一個 面向對象 的映射器,用作數據模型(以 Python 類的形式定義)和關係型數據庫間的介質;一個基於正則表達式的 URL 分發器;一個視圖系統,用於處理請求;以及一個模板系統。

核心框架中還包括:

  • 一個輕量級的、獨立的 Web 服務器,用於開發和測試。
  • 一個表單序列化及驗證系統,用於 HTML 表單和適於數據庫存儲的數據之間的轉換。
  • 一個緩存框架,並有幾種緩存方式可供選擇。
  • 中間件支持,允許對請求處理的各個階段進行干涉。
  • 內置的分發系統允許應用程序中的組件採用預定義的信號進行相互間的通信。
  • 一個序列化系統,能夠生成或讀取採用 XML 或 JSON 表示的 Django 模型實例。
  • 一個用於擴展模板引擎的能力的系統。

Django 包含了很多應用在它的 contrib 包中,這些包括:

  • 一個可擴展的認證系統
  • 動態站點管理頁面
  • 一組產生 RSS 和 Atom 的工具
  • 一個靈活的評論系統
  • 產生 Google 站點地圖(Google Sitemaps)的工具
  • 防止跨站請求僞造(cross-site request forgery)的工具
  • 一套支持輕量級標記語言(Textile 和 Markdown)的模板庫
  • 一套協助創建地理信息系統(GIS)的基礎框架

Django 可以運行在啓用了 mod_python 的 Apache 2 上,或是任何 WSGI 兼容的 Web 服務器。Django 也有啓動 FastCGI 服務的能力,因此能夠應用於任何支持 FastCGI 的機器上。

下列數據庫引擎被 Django 官方支持:PostgreSQL、MySQL、SQLite、Oracle

Microsoft SQL Server 的適配器正在開發中,處於試驗階段。(注:SQL Server 的支持在 1.0 版本中已經被完全去除)

自 Django 1.0 起,已經可以利用 Jython 運行在任何 J2EE 服務器。除 CPython 外,Django 當前官方支持使用 Jython 2.7b2 運行,但不保證與 CPython 上的行爲完全兼容,並應預期一些功能(如使用 Pillow 的部分)無法使用。

Django做什麼?

Django 是一個開放源代碼的高級 PYTHON WEB 開發框架,非常出色,使用 Django 的能力可能是學習 Python 的最大優勢之一。利用 Django 的模型 Model,視圖 View 和模板 Template,可以輕鬆構建可供部署的應用程序。

Python 下有許多款不同的 Web 框架。Django 是重量級選手中最有代表性的一位。許多成功的網站和 APP 都基於 Django。

Django 的主要目標是使得開發複雜的、數據庫驅動的網站變得簡單。Django 注重組件的重用性和“可插拔性”,敏捷開發和 DRY 法則(Don’t Repeat Yourself)。在 Django 中 Python 被普遍使用,甚至包括配置文件和數據模型。

Django 框架的核心包括:一個 面向對象 的映射器,用作數據模型(以 Python 類的形式定義)和關係型數據庫間的介質;一個基於正則表達式的 URL 分發器;一個視圖系統,用於處理請求;以及一個模板系統。

Python中的GIL

在Python中GIL是Global Interpreter Lock,即全局解釋鎖的縮寫,保證了同一時刻只有一個線程在一個CPU上執行字節碼,無法將多個線程映射到多個CPU上。這是CPython解釋器的缺陷,由於CPython是大部分環境下默認的Python執行環境,而很多庫都是基於CPython編寫的,因此很多人將GIL歸結爲Python的問題。

這也是使得標準版本的Python並不能實現真正的多線程併發的直接原因。簡單來說就是,一個Python進程永遠不能在同一時刻使用多個CPU核心。GIL被設計來保護線程安全,由於多線程共享變量,如果不能很好的進行線程同步,多線程非常容易將線程改亂。

官方解釋:在cpython中,全局解釋器鎖(gil)是一個互斥體,它阻止多個本機線程同時執行python字節碼。這個鎖是必要的,主要是因爲cpython的內存管理不是線程安全的。(然而,由於gil的存在,其他特性已經依賴於它所執行的保證。)

CGI

什麼是CGI

CGI全稱是“公共網關接口”(Common Gateway Interface),HTTP服務器與你的或其它機器上的程序進行“交談”的一種工具,其程序須運行在網絡服務器上。
CGI可以用任何一種語言編寫,只要這種語言具有標準輸入、輸出和環境變量。如php,perl,tcl等。

什麼是FastCGI

FastCGI像是一個常駐(long-live)型的CGI,它可以一直執行着,只要激活後,不會每次都要花費時間去fork一次(這是CGI最爲人詬病的fork-and-execute 模式)。它還支持分佈式的運算, 即 FastCGI 程序可以在網站服務器以外的主機上執行並且接受來自其它網站服務器來的請求。
FastCGI是語言無關的、可伸縮架構的CGI開放擴展,其主要行爲是將CGI解釋器進程保持在內存中並因此獲得較高的性能。衆所周知,CGI解釋器的反覆加載是CGI性能低下的主要原因,如果CGI解釋器保持在內存中並接受FastCGI進程管理器調度,則可以提供良好的性能、伸縮性、Fail- Over特性等等。

FastCGI與CGI特點

1、如CGI,FastCGI也具有語言無關性.
2、如CGI, FastCGI在進程中的應用程序,獨立於核心web服務器運行,提供了一個比API更安全的環境。(APIs把應用程序的代碼與核心的web服務器鏈接在一起,這意味着在一個錯誤的API的應用程序可能會損壞其他應用程序或核心服務器; 惡意的API的應用程序代碼甚至可以竊取另一個應用程序或核心服務器的密鑰。)
3、FastCGI技術目前支持語言有:C/C++、Java、Perl、Tcl、Python、SmallTalk、Ruby等。相關模塊在Apache, ISS, Lighttpd等流行的服務器上也是可用的。
4、如CGI,FastCGI的不依賴於任何Web服務器的內部架構,因此即使服務器技術的變化, FastCGI依然穩定不變。

FastCGI的工作原理

1、Web Server啓動時載入FastCGI進程管理器(IIS ISAPI或Apache Module)
2、FastCGI進程管理器自身初始化,啓動多個CGI解釋器進程(可見多個php-cgi)並等待來自Web Server的連接。
3、當客戶端請求到達Web Server時,FastCGI進程管理器選擇並連接到一個CGI解釋器。Web server將CGI環境變量和標準輸入發送到FastCGI子進程php-cgi。
4、FastCGI子進程完成處理後將標準輸出和錯誤信息從同一連接返回Web Server。當FastCGI子進程關閉連接時,請求便告處理完成。FastCGI子進程接着等待並處理來自FastCGI進程管理器(運行在Web Server中)的下一個連接。 在CGI模式中,php-cgi在此便退出了。
在上述情況中,你可以想象CGI通常有多慢。每一個Web請求PHP都必須重新解析php.ini、重新載入全部擴展並重初始化全部數據結構。使用FastCGI,所有這些都只在進程啓動時發生一次。一個額外的好處是,持續數據庫連接(Persistent database connection)可以工作。

FastCGI的不足

因爲是多進程,所以比CGI多線程消耗更多的服務器內存,PHP-CGI解釋器每進程消耗7至25兆內存,將這個數字乘以50或100就是很大的內存數。
Nginx 0.8.46+PHP 5.2.14(FastCGI)服務器在3萬併發連接下,開啓的10個Nginx進程消耗150M內存(15M10=150M),開啓的64個php-cgi進程消耗1280M內存(20M64=1280M),加上系統自身消耗的內存,總共消耗不到2GB內存。如果服務器內存較小,完全可以只開啓25個php-cgi進程,這樣php-cgi消耗的總內存數才500M。
上面的數據摘自Nginx 0.8.x + PHP 5.2.13(FastCGI)搭建勝過Apache十倍的Web服務器(第6版)

什麼是PHP-CGI

PHP-CGI是PHP自帶的FastCGI管理器
PHP-CGI的不足
1、php-cgi變更php.ini配置後需重啓php-cgi才能讓新的php-ini生效,不可以平滑重啓
2、直接殺死php-cgi進程,php就不能運行了。(PHP-FPM和Spawn-FCGI就沒有這個問題,守護進程會平滑從新生成新的子進程。)

什麼是PHP-FPM

PHP-FPM是一個PHP FastCGI管理器,是隻用於PHP的,可以在 http://php-fpm.org/download 下載得到.
PHP-FPM其實是PHP源代碼的一個補丁,旨在將FastCGI進程管理整合進PHP包中。必須將它patch到你的PHP源代碼中,在編譯安裝PHP後纔可以使用。
相對Spawn-FCGI,PHP-FPM在CPU和內存方面的控制都更勝一籌,而且前者很容易崩潰,必須用crontab進行監控,而PHP-FPM則沒有這種煩惱。
PHP5.3.3已經集成php-fpm了,不再是第三方的包了。PHP-FPM提供了更好的PHP進程管理方式,可以有效控制內存和進程、可以平滑重載PHP配置,比spawn-fcgi具有更多有點,所以被PHP官方收錄了。在./configure的時候帶 –enable-fpm參數即可開啓PHP-FPM。

什麼是Spawn-FCGI

Spawn-FCGI是一個通用的FastCGI管理服務器,它是lighttpd中的一部份,很多人都用Lighttpd的Spawn-FCGI進行FastCGI模式下的管理工作,不過有不少缺點。而PHP-FPM的出現多少緩解了一些問題,但PHP-FPM有個缺點就是要重新編譯,這對於一些已經運行的環境可能有不小的風險(refer),在php 5.3.3中可以直接使用PHP-FPM了。
Spawn-FCGI目前已經獨成爲一個項目,更加穩定一些,也給很多Web 站點的配置帶來便利。已經有不少站點將它與nginx搭配來解決動態網頁。
最新的lighttpd也沒有包含這一塊了(http://www.lighttpd.net/search?q=Spawn-FCGI ),但可以在以前版本中找到它。在lighttpd-1.4.15版本中就包含了(http://www.lighttpd.net/download/lighttpd-1.4.15.tar.gz)
目前Spawn-FCGI的下載地址是http://redmine.lighttpd.net/projects/spawn-fcgi ,最新版本是http://www.lighttpd.net/download/spawn-fcgi-1.6.3.tar.gz
注:最新的Spawn-FCGI可以到lighttpd.net網站搜索“Spawn-FCGI”找到它的最新版本發佈地址

PHP-FPM與spawn-CGI對比測試

PHP-FPM的使用非常方便,配置都是在PHP-FPM.ini的文件內,而啓動、重啓都可以從php/sbin/PHP-FPM中進行。更方便的是修改php.ini後可以直接使用PHP-FPM reload進行加載,無需殺掉進程就可以完成php.ini的修改加載
結果顯示使用PHP-FPM可以使php有不小的性能提升。PHP-FPM控制的進程cpu回收的速度比較慢,內存分配的很均勻。
Spawn-FCGI控制的進程CPU下降的很快,而內存分配的比較不均勻。有很多進程似乎未分配到,而另外一些卻佔用很高。可能是由於進程任務分配的不均勻導致的.而這也導致了總體響應速度的下降。而PHP-FPM合理的分配,導致總體響應的提到以及任務的平均。

uwsgi

基於Python的Web項目部署起來真是頭痛,常見的部署方法有:

◆fcgi:用spawn-fcgi或者框架自帶的工具對各個project分別生成監聽進程,然後和http服務互動。

◆wsgi:利用http服務的mod_wsgi模塊來跑各個project。

無論哪種都很麻煩,apache的mod_wsgi配置起來麻煩,內存佔用還大,如果要加上nginx作爲靜態頁面的服務器那就更麻煩了;

如果Python中能有個什麼東西像php-cgi一樣監聽同一端口,進行統一管理和負載平衡,那真是能省下大量的部署功夫。這就是uwsgi爲什麼會誕生!

uWSGI 是一個“旨在開發用於構建託管服務的完整堆棧”的軟件應用程序。它以 Web 服務器網關接口命名,這是該項目支持的第一個插件。 uWSGI 通常用於與 Web 服務器(如 Cherokee 和 Nginx)一起提供 Python Web 應用程序,後者爲 uWSGI 的本機 uwsgi 協議提供直接支持。

uWSGI 是一個 Web 服務器,它實現了 WSGI 協議、uwsgi、http 等協議。Nginx 中 HttpUwsgiModule 的作用是與 uWSGI 服務器進行交換。

要注意 WSGI / uwsgi / uWSGI 這三個概念的區分。

  • WSGI 是一種通信協議。
  • uwsgi 是一種線路協議而不是通信協議,在此常用於在 uWSGI 服務器與其他網絡服務器的數據通信。
  • 而 uWSGI 是實現了 uwsgi 和 WSGI 兩種協議的 Web 服務器。

uwsgi 協議是一個 uWSGI 服務器自有的協議,它用於定義傳輸信息的類型(type of information),每一個 uwsgi packet 前 4byte 爲傳輸信息類型描述,它與 WSGI 相比是兩樣東西。

uWSGI,既不用wsgi協議也不用fcgi協議,而是自創了一個uwsgi的協議,據說該協議大約是fcgi協議的10倍那麼快。

uWSGI的主要特點如下:

◆超快的性能。

◆低內存佔用(實測爲apache2的mod_wsgi的一半左右)。

◆多app管理。

◆詳盡的日誌功能(可以用來分析app性能和瓶頸)。

◆高度可定製(內存大小限制,服務一定次數後重啓等)。

django 項目中uwsgi的process參數設置

由於Python中GIL的存在,所以爲了提高併發通常使用多進程運行web程序,那麼在uwsgi中該將process設置成多少呢,查閱網上,一般都是設置4個進程2個線程,還有的建議將進程數設置爲CPU核心數的兩倍。我們分析一下:假設一次http請求響應時間是50ms,那麼在單個進程的情況下,一秒最多處理20個請求,如果有n個進程同時處理,那就是1秒內能20*n個請求。但是要注意,進程數可不是隨意設置的,當你的電腦有4個核心時,你開4個進程,可以將CPU全部利用上,開的再多,由於沒有更多的核心去運行進程,所以這些進程並不能同時運行,只能每個執行一段時間後,讓其他進程運行,這樣也就增加了單個請求的響應時間,比如設置成將uwsgi設置爲8進程,這時平均響應時間大約在100ms。所以,在處理併發的時候,要考慮響應時間。在響應時間一定的情況下,單個http請求的響應時間越小,能夠處理的併發越大。
下面做測試驗證一下
測試機器:i5-3210(雙核四線程) 內存(6G)單次http請求響應時間大約16ms (該接口基本不涉及磁盤IO,所以uwsgi沒有使用多線程)
這裏需要注意:上面的CPU可以理解成是四核心,但是性能會比真正的四核有損失,大約30%,具體百度。
設置uwsgi進程數爲4,用ab測試,在2個、4個、8個併發的情況下的響應時間和QPS,理論上響應時間大約爲
16 、 23 、45 。我們看一下實際情況:
這是併發爲2的情況:

Concurrency Level:      2
Time taken for tests:   0.896 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      1157000 bytes
HTML transferred:       1119100 bytes
Requests per second:    111.55 [#/sec] (mean)
Time per request:       17.929 [ms] (mean)
Time per request:       8.964 [ms] (mean, across all concurrent requests)
Transfer rate:          1260.40 [Kbytes/sec] received

這是併發爲4的情況:

Concurrency Level:      4
Time taken for tests:   0.638 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      1157000 bytes
HTML transferred:       1119100 bytes
Requests per second:    156.84 [#/sec] (mean)
Time per request:       25.504 [ms] (mean)
Time per request:       6.376 [ms] (mean, across all concurrent requests)
Transfer rate:          1772.09 [Kbytes/sec] received

這是併發爲8的情況:

Concurrency Level:      8
Time taken for tests:   0.636 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      1157000 bytes
HTML transferred:       1119100 bytes
Requests per second:    157.19 [#/sec] (mean)
Time per request:       50.894 [ms] (mean)
Time per request:       6.362 [ms] (mean, across all concurrent requests)
Transfer rate:          1776.05 [Kbytes/sec] received

可以看到上圖基本驗證了我的猜測。並且在併發爲4的時候就達到了QPS的最高值,此時再繼續增大併發,只會增加單個http請求的響應時間。我們在保證響應時間在200ms的情況,通過上面的數據,估計最高併發大約爲30 ,接着去驗證一下:

Concurrency Level:      30
Time taken for tests:   0.649 seconds
Complete requests:      100
Failed requests:        0
Total transferred:      1157000 bytes
HTML transferred:       1119100 bytes
Requests per second:    154.10 [#/sec] (mean)
Time per request:       194.679 [ms] (mean)
Time per request:       6.489 [ms] (mean, across all concurrent requests)
Transfer rate:          1741.15 [Kbytes/sec] received

因爲,以後在設置uwsgi參數時,將process設置爲CPU的核心數就可以了,如果涉及到從磁盤讀取數據的情況,可以考慮加上線程。如果想增大併發能力就要辦法降低單個http請求的響應時間。

綁定物理核

另一個提高性能的方法那就是綁定物理核心

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章