橫向壓力測試:Ruby on Rails PK CakePHP
Ruby on Rails以優(yōu)雅的MVC架構(gòu)聞名,這個架構(gòu)如此誘人和美麗,而CakePHP則是PHP開發(fā)中常用的框架之一。如果你不想束縛于傳統(tǒng)的PHP的磚頭式開發(fā),那么你可以嘗試轉(zhuǎn)向MVC架構(gòu),不過Rails的性能和部署問題一直讓人擔(dān)心。
兩者對比的話題在網(wǎng)上眾說紛紜,很少見到客觀而有說服力的論證和充分模擬實(shí)際環(huán)境下的壓力測評。作為架構(gòu)選型的重要決定,我們既不能人云亦云,更不可憑空臆想,一定要有充分的測試數(shù)據(jù)才能幫助做出正確的決定。
心動不如行動,立刻著手安排了仿真環(huán)境測試。第一步是設(shè)計測試方案:
壓力測試的目標(biāo)集中在Ruby On Rails和CakePHP的效率,所以采用同樣的Nginx生產(chǎn)環(huán)境,但避開所有數(shù)據(jù)庫操作以避免瓶頸轉(zhuǎn)嫁到數(shù)據(jù)庫影響結(jié)果。
代碼的主要部分都是通過輸出128000個4位的十進(jìn)制隨機(jī)數(shù),來模擬總計約500KB的頁面數(shù)據(jù)輸出。調(diào)用的指令都很基本,對腳本測試來說很公平。
不過既然是虛擬高壓力測試,實(shí)際環(huán)境中數(shù)據(jù)庫讀寫等操作的時間開銷應(yīng)該有一個仿真替代,所以通過Sleep 200ms來仿真具有高度數(shù)據(jù)壓力的服務(wù)端。當(dāng)然我們都知道Sleep是沒有真實(shí)的cpu開銷的,所以不會影響測試結(jié)果的公平。
測試工具使用經(jīng)典的ApacheBench。先后測試10并發(fā)100請求(-c 10 -n 100) 的中等壓力,和200并發(fā)5000請求(-c 200 -n 5000)高壓測試。
環(huán)境
- OS: FreeBSD 8.1
- CPU: Intel 4核心 Core 2
- RAM: 4GB 內(nèi)存
- PHP環(huán)境:nginx+php-fpm(5.3.3)+APC
- Rails環(huán)境:nginx+passenger+Ruby(1.8.7) on Rails(3.0.0)
- 所有軟件均使用ports安裝
fpm的優(yōu)化配置:
- pm.max_children = 1000
- pm.start_servers = 20
- pm.min_spare_servers = 5
- pm.max_spare_servers = 1000
passenger的優(yōu)化配置(nginx.conf):
passenger_max_pool_size 300;//4GB內(nèi)存最大的允許值,再追加便無法啟動passenger
通過Rails腳本創(chuàng)建Test App:
rails new dummy
Ruby on Rails 代碼:
- // app/controller/test_controller.rb
- class TestController < ApplicationController
- def index
- sleep(0.2)
- end
- end// app/views/test/index.html.rb
- <% 128000.times do %><%=rand(8999)+1000%><% end %>
PHP代碼:
- // vsruby.php
- php
- usleep(200000);
- echo "<html><head>head><body>";
- for($i = 0; $i < 128000;$i++)
- {
- echo mt_rand(8999,9999);
- }
- echo "body>html>";
CakePHP代碼:
- // CakePHP
- // app/controller/test_controller.php
- php
- class TestController extends AppController {
- var $name = 'Test';
- function index()
- {
- usleep(200000);
- }
- } // CakePHP
- // app/views/test/index.ctp
- php
- for($i = 0; $i < 128000;$i++)
- {
- echo mt_rand(8999,9999);
- }
- ?>
#p#
10并發(fā)100個請求:
- // Ruby on Rails
- // CPU usage: 100%
- Server Software: nginx/0.8.52
- Server Hostname: 127.0.0.1
- Server Port: 80
- Document Path: /test/
- Document Length: 512731 bytes
- Concurrency Level: 10
- Time taken for tests: 40.939 seconds
- Complete requests: 100
- Failed requests: 0
- Write errors: 0
- Total transferred: 51334500 bytes
- HTML transferred: 51273100 bytes
- Requests per second: 2.44 [#/sec] (mean)
- Time per request: 4093.898 [ms] (mean)
- Time per request: 409.390 [ms] (mean, across all concurrent requests)
- Transfer rate: 1224.54 [Kbytes/sec] received
- Connection Times (ms)
- min mean[+/-sd] median max
- Connect: 0 0 0.0 0 0
- Processing: 1231 4036 3167.1 3149 16396
- Waiting: 1203 2428 2533.7 1625 15683
- Total: 1231 4036 3167.1 3150 16396
- Percentage of the requests served within a certain time (ms)
- 50% 3150
- 66% 3353
- 75% 3679
- 80% 3893
- 90% 12307
- 95% 12307
- 98% 16108
- 99% 16396
- 100% 16396 (longest request)//php
- //CPU usage: 20-30%
- Server Software: nginx/0.8.52
- Server Hostname: 127.0.0.1
- Server Port: 80
- Document Path: /php/
- Document Length: 512039 bytes
- Concurrency Level: 10
- Time taken for tests: 4.144 seconds
- Complete requests: 100
- Failed requests: 0
- Write errors: 0
- Total transferred: 51218600 bytes
- HTML transferred: 51203900 bytes
- Requests per second: 24.13 [#/sec] (mean)
- Time per request: 414.389 [ms] (mean)
- Time per request: 41.439 [ms] (mean, across all concurrent requests)
- Transfer rate: 12070.36 [Kbytes/sec] received
- Connection Times (ms)
- min mean[+/-sd] median max
- Connect: 0 0 0.1 0 0
- Processing: 400 405 14.0 403 502
- Waiting: 201 205 3.1 204 218
- Total: 400 405 14.0 403 502
- Percentage of the requests served within a certain time (ms)
- 50% 403
- 66% 404
- 75% 405
- 80% 405
- 90% 408
- 95% 409
- 98% 501
- 99% 502
- 100% 502 (longest request)// CakePHP
- Server Software: nginx/0.8.52
- Server Hostname: 127.0.0.1
- Server Port: 80
- Document Path: /cakephp/
- Document Length: 512652 bytes
- Concurrency Level: 10
- Time taken for tests: 4.036 seconds
- Complete requests: 100
- Failed requests: 0
- Write errors: 0
- Total transferred: 51291900 bytes
- HTML transferred: 51265200 bytes
- Requests per second: 24.78 [#/sec] (mean)
- Time per request: 403.553 [ms] (mean)
- Time per request: 40.355 [ms] (mean, across all concurrent requests)
- Transfer rate: 12412.20 [Kbytes/sec] received
- Connection Times (ms)
- min mean[+/-sd] median max
- Connect: 0 0 0.7 0 6
- Processing: 302 399 119.1 363 775
- Waiting: 275 370 119.9 340 764
- Total: 302 400 119.1 364 775
- Percentage of the requests served within a certain time (ms)
- 50% 364
- 66% 372
- 75% 378
- 80% 381
- 90% 725
- 95% 755
- 98% 775
- 99% 775
- 100% 775 (longest request)
5000個請求,200并發(fā)數(shù):
- // php
- Server Software: nginx/0.8.52
- Server Hostname: 127.0.0.1
- Server Port: 80
- Document Path: /php/
- Document Length: 512039 bytes
- Concurrency Level: 200
- Time taken for tests: 82.243 seconds
- Complete requests: 5000
- Failed requests: 0
- Write errors: 0
- Total transferred: 2560930000 bytes
- HTML transferred: 2560195000 bytes
- Requests per second: 60.80 [#/sec] (mean)
- Time per request: 3289.722 [ms] (mean)
- Time per request: 16.449 [ms] (mean, across all concurrent requests)
- Transfer rate: 30408.75 [Kbytes/sec] received
- Connection Times (ms)
- min mean[+/-sd] median max
- Connect: 0 1 1.6 0 20
- Processing: 405 3258 4830.3 2675 56787
- Waiting: 202 1048 1324.8 344 53432
- Total: 405 3259 4830.3 2676 56787
- Percentage of the requests served within a certain time (ms)
- 50% 2676
- 66% 3081
- 75% 3361
- 80% 3535
- 90% 3828
- 95% 4262
- 98% 5709
- 99% 31863
- 100% 56787 (longest request) // CakePHP
- Server Software: nginx/0.8.52
- Server Hostname: 127.0.0.1
- Server Port: 80
- Document Path: /cakephp/
- Document Length: 512652 bytes
- Concurrency Level: 200
- Time taken for tests: 99.652 seconds
- Complete requests: 5000
- Failed requests: 0
- Write errors: 0
- Total transferred: 2565102923 bytes
- HTML transferred: 2563767656 bytes
- Requests per second: 50.17 [#/sec] (mean)
- Time per request: 3986.073 [ms] (mean)
- Time per request: 19.930 [ms] (mean, across all concurrent requests)
- Transfer rate: 25137.36 [Kbytes/sec] received
- Connection Times (ms)
- min mean[+/-sd] median max
- Connect: 0 4 57.8 0 1663
- Processing: 367 3969 1825.7 3857 10630
- Waiting: 280 1543 731.9 1297 3953
- Total: 472 3973 1824.8 3860 10630
- Percentage of the requests served within a certain time (ms)
- 50% 3860
- 66% 4466
- 75% 5065
- 80% 5426
- 90% 6482
- 95% 7337
- 98% 8599
- 99% 8847
- 100% 10630 (longest request)
- // Rails
- //約10分鐘后,服務(wù)器進(jìn)入假死狀態(tài)。
#p#
備注:
因?yàn)椴惶嘈舝uby的性能會有這樣大的差距,懷疑是否ruby的rand()效率格外的低造成問題,我在測試完成又將rand()去掉,改為直接輸出數(shù)字,但腳本執(zhí)行時間并沒有明顯縮短。所以應(yīng)該說是 ruby 對循環(huán)或數(shù)據(jù)輸出的處理效率不佳導(dǎo)致。
結(jié)論
坦白說,幾個ab測試跑下來,ruby的成績?nèi)绱酥?,不?0并發(fā)的100請求就已經(jīng)用滿了服務(wù)器資源,更甚至沒有能通過200并發(fā)5000請求的高壓測試,這把我自己也嚇了一跳。想到堅持在使用Rails的twitter,心中的敬佩油然而生。不知道那是什么樣的硬件或軟件優(yōu)化,才可以用Ruby來支撐那樣巨大的訪問量。
客觀的從純性能的角度出發(fā),在生產(chǎn)環(huán)境中,Ruby/Rails還是只適合Small Business。對與壓力較高的服務(wù)或應(yīng)用,就必須投入大量額外的硬件資源才能維持。本次測試中,Ruby On Rails與CakePHP的性能差距達(dá)到10倍之多,實(shí)在讓我不敢考慮把Rails用在生產(chǎn)環(huán)境中。另一方面,PHP依托龐大的社區(qū),多年來積累了眾多的優(yōu)化手段,其性能領(lǐng)先也有它的道理。在PHP架構(gòu)之上的MVC候選人CakePHP,性能雖然相對于傳統(tǒng)php的代碼書寫方法略有損失,但這個損失不到10%。所以對于在考慮MVC架構(gòu)的php用戶來說,CakePHP的性能完全在可以接受的范圍內(nèi)。
原文鏈接:http://blog.splayer.org/index.php/2010/10/performance-penalty-in-mvc-web-deployment/
所有測試源代碼和nginx/php-fpm配置文件下載:http://blog.splayer.org/wp-content/uploads/2010/10/FBSD-SandBox.zip
【編輯推薦】
- Ruby On Rails開發(fā)教程
- 在Nginx上運(yùn)行Ruby on Rails
- Ruby on Rails性能優(yōu)化七劍
- 詳細(xì)剖析Ruby on Rails配置文件