qileilove

          blog已經轉移至github,大家請訪問 http://qaseven.github.io/

          Django性能測試—一個現實世界的例子

           大約一個星期前,安德魯 和 我 啟動 一個新的 Django 打造的網站,站名叫 Hey!Wall 。這是一個按照社交網絡中的“墻”的概念建立的社交網站,它為各類朋友提供了一個留言及分享照片、視頻和鏈接的空間。
            我們想對其進行性能評估,并進行一些服務器配置和代碼修改來決定采取何種步驟進行改進。我們使用 httperf 進行了測試,并通過優化將其性能提高了整整一倍。
            服務器和客戶端
            服務器一是一臺 Slicehost 提供的 Xen VPS ,配有 256MB 內存,運行的是 Debian Etch 系統。部署在美國中西部。
            為了測試,采用了一臺位于英國的 Xtraordinary Hosting 提供的 Xen VPS,作為客戶端。通常我們使用的 ADSL 訪問互聯網絡,但這讓我們很難向服務器發起足夠多的訪問請求。使用連接良好的 VPS 作為客戶端使我們可以真正地對服務器加以考驗。
            服務器規格說明
            很難確切地描述服務器的規格。該 VPS 配有 256MB 內存,與數個類似的 VPS 同居一臺主機(大概是一臺 裝有 16GB 內存的 Quad Core 服務器)之上。假定裝滿了 256MB 切片的話,物理服務器上最多裝有 64 臺 VPS 。如果四個處理器都是 2.4GHz,那么共 9.6 GHz ,除以 64 得到最少 150MHz 的 CPU 。
            在 Xen VPS 上,無需競爭你就可以獲得穩定的內存和 CPU 分配,但通常 主機上任何空閑的 CPU 都將得到使用。如果在同一機器上的其它 VPS 處于空閑狀態,你的 VPS 將能夠使用更多的 CPU 。這也許意味著在測試期間使用了更多的 CPU ,即某些測試可能比其它的使用了更多的 CPU 資源。
            使用 httperf 評估性能
            現有各式各樣的網絡性能測試工具,主要包括 ab (來自 Apache), Flood 和 httperf。我們使用 httperf 并沒有任何特別理由。
            httperf 命令看起來如下所示:
            httperf --hog --server=example.com --uri=/ --timeout=10 --num-conns=200 --rate=5
            在該例中,我們向 http://example.com/ 發起了 200 次訪問請求,每秒最多 5 次。
            測試計劃
            某些工具支持進程,可以模仿用戶對網站提交任務。我們使用了一種簡單的“暴力”測試來了解該站點每秒能夠處理多少請求。
            該基本方法是發起一定數量的請求,能夠判斷服務器如何反應:狀態 200 為成功,狀態 500 為失敗。提高頻率(每秒制造的請求數量)然后再試一遍。如果開始返回大量的  500 ,則已經達到極限。
            監測服務器資源
            另一個方面是要掌握服務器在內存和 CPU 使用方面的情況。要跟蹤這一情況,我們運行 top 并將輸出記錄為日志文件以供稍后查閱。該 top 命令如下所示:
            top -b -d 3 -U www-data > top.txt
            在該例中,我們以用戶 www-data 每三秒記錄一次進程的日志信息。如果你想更加明確的指定目標,可以使用 -p 1, 2, 3 ,而不是 -U username ,其中 1、2 和 3 是 pid(即要觀測進程的進程ID)。
            網頁服務器為配有以 FastCGI processes 方式運行的 Python 2.5 的 Lighttpd 。盡管數據庫的日志也是很有用的信息,但我們沒有記錄該進程(PostgreSQL)的日志。
            另一個有用的工具是 vmstat,特別是 swap 列顯示了有多少內存進行了交換。交換的意思是你沒有足夠的內存,它是一種性能殺手。要想反復運行 vmstat 的話,必須指定每次檢查間隔的秒數。如:
            vmstat 2
            使用 httperf 進行已認證訪問
            httperf 只是簡單地向某個 URL 發出簡單的 GET 請求,并下載  html  文本(但不包括任何媒體文件)。對公共/匿名(public/anonymous)網頁發起的訪問請求是件輕松的事情,但如果要訪問需要登錄的頁面怎么辦呢?
            httperf 可以傳遞請求頭部信息。Django 身份校驗( authentication)(由 django.contrib.auth 提供)使用的進程依賴于在客戶端 cookie 中所保存的進程  id 。而客戶端在請求的頭部信息中傳遞 cookie 。你可以看到這一切是如何進行的。
            登錄站點,查看 cookies 信息。其中應該有個類似 sessionid=97d674a05b2614e98411553b28f909de 的數值。要通過 httperf 傳遞該 cookie,可以使用 --add-header 參數選項。如:
            httperf ... --add-header='Cookie: sessionid=97d674a05b2614e98411553b28f909den'
            小心頭部信息之后的 n 。如果少了該字符,你的每個請求可能都會返回超時信息。
           測試哪個頁面?
            考慮到這一點,我們測試了網站的兩個網頁:
            主頁: 對主頁的匿名訪問
            “墻”: 對某個“墻”已認證訪問,該網頁包括從數據庫獲取的內容
            事實靜態與高度動態
            對于匿名用戶來說,主頁基本上是靜態的,它只是簡單的渲染某個模板而無需數據庫的任何數據。
            “墻”頁面則非常動態,包括了從數據庫獲取的主要數據。該模板在被渲染時,針對不同時區用戶的日期設置“刪除”了指向某些物件的鏈接,等等。某些“墻”包含了大約50個物件,在被優化前,大約要發起 80 條數據庫查詢。
            第一次測試時,我們運行了兩個可以從 Django 接受請求 FastCGI 后端。
            Home: 175 req/s (即每秒請求數量)Wall: 8 req/s.
            經壓縮的內容
            第一個配置優化是使用 GZipMiddleware 激活輸出的 gzip  壓縮。性能輕微提高,沒有大的變化。但無論如何為了節約帶寬,這么做還是值得的。
            Home: 200 req/s.
            Wall: 8 req/s.
            更多進程,更短的隊列
            然后,我們將 FastCGI 后端的數量從 2 個提升到 5 個。這項改進減少了 500 響應的數量,因為更多的請求可以由額外的后端來處理。
            Home: 200 req/s.
            Wall: 11 req/s.
            更多的進程,更多的問題
            從 2 到 5 的改進非常不錯,因此我們決定將  FastCGI 后端數量提升到 10 。性能卻顯著地 下降 了。
            經查看服務器上的 vmstat ,可以看到原因是出現了內存交換。太多的進程,每個都為 Python 使用了內存,導致 VPS 內存耗盡,從而不得不從硬盤往返交換內存。
            Home: 150 req/s.
            Wall: 7 req/s.
            基于此,我們將 FastCGI 后端數量降回 5 以進行更多測試。
            分析——時間耗到哪里去了
            “墻”頁面的性能令人失望,因此我們開始進行優化。我們所做第一件事情是分析代碼以確定時間都被花費在何處。
            使用一些簡單的 分析中間件 之后,很清楚地發現時間被消耗在數據庫查詢之上。“墻”頁面包括許多查詢,且數量與其所包含的物件數量呈正比。測試墻頁面上引發了大約 80 個查詢。毫無疑問其性能是糟糕的。
            進行優化
            通過優化物件附加媒體的處理方式,我們直接給每個物件剔除了一次查詢。該措施稍微地減少了請求所需時間,因此也提高了每秒可處理的查詢數量。
            Wall: 12 req/s.
            導致低效的另一個原因是無論頁面是否被請求,對每個物件的內容都應用了多個過濾器(Filter)。經我們修改,被過濾內容的 HTML 輸出都被存儲在物件當中,節約了頁面被查閱時的需要進程。這又帶來一點小小改進。
            Wall: 13 req/s.
            通過減少數據庫查詢,我們以修改用戶配置文件(用于顯示是誰將該物件粘貼到墻上)的獲取方式為每個物件剔除了一次查詢。這次修改又提高了不少。
            Wall: 15 req/s.
            這場測試的最后一次優化目標是減少獲取物件所附加媒體的查詢數量。我們再一次削減了一些查詢,稍微地提高了性能。
            Wall: 17 req/s.
            下一步:緩存
            在盡可能地減少查詢之后,接下來要進行一些緩沖工作。獲取緩存數據通常比查詢數據庫更加快捷,因此我們期待性能有一個顯著提升。
            對整個頁面的輸出進行緩存是沒有意義的,因為每個頁面根據發出請求的用戶不同而截然不同。只有當用戶對同一頁面的兩次請求之間,情況沒有發生任何變化,才可能出現緩存命中。
            對墻、物件及用戶的列表進行緩存的作用更大。被緩存的數據將被用于從同一用戶發出的多個請求,及在對于墻壁的不同程度和不同用戶訪問之間共享。這未必是巨大的勝利,因為每個“墻”很可能只有極少數的用戶,而數據必須在高速緩存中停留足夠長的時間以被別人獲取。
            在這種情況下,我們簡化的 httperf 測試將會被極大地誤導。每個請求都由同一用戶發出,因此緩存命中幾乎是100%,而性能將因此極高!這反映不出真實世界的站點使用情況,因此我們最好進行一些更好的測試。
            目前我們還沒有使用緩存,因為站點可以輕松地應對的當前活動水平,但一旦 Hey! Wall 流行起來,這將是我們的下一個步驟。
            多少用戶能夠導致每秒17次請求?
            提供每秒 17 次請求相應看起來仍然非常少,但將該數據翻譯成站點的實際用戶量是非常有趣的事情。顯然,這數據不包括提供像圖片、CSS 和 JavaScript 文件之類的媒體文件服務。相對來說,媒體文件個頭要大一些,但它們直接由 Lighttpd (而不是 Django)處理,并提供了 Expires 頭部信息來允許客戶端對它們進行緩存。不過,為了在測試中更好地進行評估,我們還是需要對服務器進行一些處理。
            現在說采用何種通用模式還為時過早,因此我說的只能是猜測。請允許我這么說!
            我將假定每個用戶平均訪問三個“墻”,并按順序查看它們的內容,暫停10至20秒時間以閱讀新的評論,或查看一些照片和打開一些鏈接。該用戶每天進行三次這種操作。
            只看墻頁面,不看媒體的話,用戶將每天對墻頁面發起 9 次訪問請求。每個用戶一次只能發起一次訪問請求,因此在時間上,任何一秒內 17 個用戶可以同時進行該操作。一分鐘內,用戶只發出3次訪問請求,因此17個并發用戶只用去了60 秒中的 3 秒(或20秒中的1秒)。
            如果一段時間內用戶的請求分布是完全平衡的(提示:不可能的!),那也就意味著每分鐘可以有 340 用戶(17 * 20)訪問該網站。延續這個不真實的例子,我們可以說每天有 1440 分鐘,而每個用戶每天訪問網站 3 分鐘,因此該網站可以應對大約 163,000 個用戶。這對于一個每月 20 美元的 VPS 來說已經非常棒了!
            為了更多統計一下這些數字,讓我們假定每天6小時內,我們每分鐘應對 200 個并發用戶,另 6 個小時內(每分鐘)應對 100 個并發用戶,剩下的 12 小時內(每分鐘)應對 10 個并發用戶。基于每秒 17 次請求的最大負荷,網站每天仍然可以應對的大約 115,000 個用戶。
            我確信這些數字并不虛假和荒謬。如果有人在評論中提出更好的評估方案或者真實世界的數據,我將非常感興趣。
            我們學到了什么?
            概括:
            測試網站性能可能會產生令人驚異的結果
            過多的數據庫查詢對性能(duh)來說不是件好事
            對站點的某類內容進行緩存比對其他一些更好
            一臺廉價的 VPS 所能應對的用戶數量比你所想像的要多得多

          posted on 2014-07-02 16:35 順其自然EVO 閱讀(345) 評論(0)  編輯  收藏 所屬分類: 性能測試

          <2014年7月>
          293012345
          6789101112
          13141516171819
          20212223242526
          272829303112
          3456789

          導航

          統計

          常用鏈接

          留言簿(55)

          隨筆分類

          隨筆檔案

          文章分類

          文章檔案

          搜索

          最新評論

          閱讀排行榜

          評論排行榜

          主站蜘蛛池模板: 南溪县| 秭归县| 江达县| 敦化市| 南华县| 泗洪县| 天等县| 芜湖市| 聂荣县| 淮南市| 韶关市| 蒙自县| 达日县| 苗栗县| 威信县| 肃南| 玉龙| 北票市| 湘潭县| 芦山县| 潢川县| 花垣县| 蓝山县| 磐石市| 舟山市| 抚顺市| 太原市| 宣汉县| 通榆县| 措美县| 浏阳市| 关岭| 十堰市| 玉田县| 信宜市| 玛多县| 克什克腾旗| 满城县| 凭祥市| 贵德县| 宝坻区|