目前所在的文章分類 ◊ apache ◊

作者: 丫忠
• 星期五, 六月 04th, 2010

丫忠最近在修改一些 Nginx 的設定,也看到了一篇關於 Nginx 不錯的文章,是一篇關於Nginx代理Web伺服器設定的測試文章,順便轉貼過來作為以後參考用。

測試目的

(1)弄清楚HTTP Upstream模塊中Server指令的max_failsfail_timeout參數的關係、它們對後端服務器健康情況的檢查起到了什麼作用、它們的取值對Http proxy模塊中的其它指令是否有直接或間接的影響等……

(2)測試HTTP Proxy模塊中proxy_next_upstream、proxy_connect_timeout、proxy_read_timeout、proxy_send_timeout指令的作用、對nginx性能的影響、對後端服務器響應的處理等……

測試方法

本文測試不會使用壓力測試,所有的測試都是通過瀏覽器手動刷新來實現的。 後端服務器使用簡單的php程序來實現。

測試環境

Nginx負載均衡/反向代理服務器
系統:CentOS 5.4 64bit
Nginx:0.7.65
IP:192.168.108.10

後端web服務器
系統:CentOS 5.4 64bit
Web環境:apache+php
Web-1 IP:192.168.108.163
Web-2 IP:192.168.108.164

本次測試主要針對HTTP Upstream和HTTP Proxy模塊進行,下面測試環境中http upstream 和http proxy模塊參數的初始化設置,後文會針對測試的參數進行相應的修改:


upstream test {
server 192.168.108.163 ;
server 192.168.108.164:80;
}

server {
listen 80;
server_name .test.com;
index index.php index.html index.htm;

location / {
proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504 http_404;

proxy_connect_timeout 10s;
proxy_read_timeout 2s;
#proxy_send_timeout 10s;
proxy_pass http://test;
}
}

提出server指令後面的參數部分,以下摘抄nginx wiki 內容

語法:server name [parameters]

parameters包含:

·weight = NUMBER -設置服務器權重,默認為1。

·max_fails = NUMBER -在一定時間內(這個時間在fail_timeout參數中設置)檢查這個服務器是否可用時產生的最多失敗請求數,默認為1,將其設置為0可以關閉檢查,這些錯誤在proxy_next_upstream或fastcgi_next_upstream (404錯誤不會使max_fails增加)中定義。

·fail_timeout = TIME -在這個時間內產生了max_fails所設置大小的失敗嘗試連接請求後這個服務器可能不可用,同樣它指定了服務器不可用的時間(在下一次嘗試連接請求發起之前),默認為10秒,fail_timeout與前端響應時間沒有直接關係,不過可以使用proxy_connect_timeout和proxy_read_timeout來控制。

·down -標記服務器處於離線狀態,通常和ip_hash一起使用。

·backup – (0.6.7或更高)只用於本服務器,如果所有的非備份服務器都宕機或繁忙。

關於max_fails參數的理解根據上面的解釋,max_fails默認為1,fail_timeout默認為10秒,也就是說,默認情況下後端服務器在10秒鐘之內可以容許有一次的失敗,如果超過1次則視為該服務器有問題,將該服務器標記為不可用。 等待10秒後再將請求發給該服務器,以此類推進行後端服務器的健康檢查。 但如果我將max_fails設置為0,則代表不對後端服務器進行健康檢查,這樣一來fail_timeout參數也就沒什麼意義了。 那若後端服務器真的出現問題怎麼辦呢? 上文也說了,可以藉助proxy_connect_timeout和proxy_read_timeout進行控制。

下面介紹http proxy模塊中的相關指令:

proxy_next_upstream
語法: proxy_next_upstream [error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_404|off]
確定在何種情況下請求將轉發到下一個服務器。 轉發請求只發生在沒有數據傳遞到客戶端的過程中。

proxy_connect_timeout
後端服務器連接的超時時間_發起握手等候響應超時時間

proxy_read_timeout
連接成功後_等候後端服務器響應時間_其實已經進入後端的排隊之中等候處理(也可以說是後端服務器處理請求的時間)

proxy_send_timeout
後端服務器數據回傳時間_就是在規定時間之內後端服務器必須傳完所有的數據

proxy_pass
這個指令設置被代理服務器的地址和被映射的URI

開始測試

情況1:後端程序執行時間超過或等於proxy_read_timeout設置值,max_fails=0 關閉後端服務器健康檢查。

Nginx配置修改內容 server 192.168.108.163 max_fails = 0;
server 192.168.108.164 max_fails = 0;
proxy_next_upstream error timeout
proxy_read_timeout 2s
後端web服務器
Web1 test.php Web2 test.php
<?php
header(‘RS:Web1′);
$t = 2;
sleep($t);
echo 『sleep {$t}s<br>』;
echo 『web-1<br>』;
?>
<?php
header(‘RS:Web2′);
$t = 5;
sleep($t);
echo 『sleep {$t}s<br>』;
echo 『web-2<br>』;
?>
備註:

我這裡的兩台後端web服務器,他們的主頁文件均為一個test.php程序,該程序分別sleep了2秒和5秒,等於和超過了proxy_read_timeout的時間,[max_fails=0] 即關閉後端服務器健康檢查。[proxy_next_upstream error timeout] 說明碰到錯誤或超時的情況切到下一個後端服務器。 如此設置後利用curl命令對nginx發起連接請求,看nginx會作何反應。

測試開始:

(1)curl -I -w %{time_total}:%{time_connect}:%{time_starttransfer}www.test.com/test.php
HTTP/1.1 504 Gateway Time-out
Server: nginx/0.7.65
Date: Tue, 18 May 2010 02:43:08 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 183
Connection: keep-alive

4.008:0.002:4.007

說明:

連續請求3次後得到的http返回結果是一樣的,均為504 Gateway Time-out 錯誤。 這種情況只有在後端服務器都有問題的時才會出現這個錯誤,很顯然我這裡的proxy_read_timeout設置的時間太短,後端程序還沒來得及把程序執行完,nginx就迫不及待的將請求甩給upstream定義的另一台服務器上了,當發現另外一台服務器同樣2秒沒有返回後,nginx這回沒有服務器可用,只有返回504 Gateway Time-out 。 這也是為什麼最後的time_total時間是4秒。 (經查看兩台web服務器的訪問日誌得知,均有一條訪問記錄,且返回代碼為200,說明nginx確實來過,但沒有等到執行完成就匆匆的離去了)如果我有3台服務器,在保證任何不變的情況下,time_total時間一定會是6秒,因為nginx會一個接一個的將3台服務器都走一遍。

————————————————– ————————————————– ——————-

好了,確認是我proxy_read_timeout設置時間太短後,我將它的值設置為3秒,再通過curl命令訪問:

(2)curl -I -w %{time_total}:%{time_connect}:%{time_starttransfer} www.test.com/test.php
HTTP/1.1 200 OK
Server: nginx/0.7.65
Date: Tue, 18 May 2010 03:07:58 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: PHP/5.1.6
RS: Web1

5.042:0.005:5.042

說明:通過3次連續請求,得到的結果是一樣的,RS:Web1 也就是說我這三次的請求都甩到了web1上。 但我web1中的程序只需要2秒後就可以返回結果,但為什麼我通過nginx代理後時間總是我的程序執行時間+proxy_read_timeout時間呢?

————————————————– ————————————————– ——————-

繼續將proxy_read_timeout設置為4s

(3)curl -I -w %{time_total}:%{time_connect}:%{time_starttransfer} www.test.com/test.php
HTTP/1.1 200 OK
Server: nginx/0.7.65
Date: Tue, 18 May 2010 03:15:25 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: PHP/5.1.6
RS: Web1

6.004:0.000:6.004

三次請求後結果也是一樣,這次花的時間更長了,但確實是程序執行時間+proxy_read_timeout 時間。 但為什麼每次都需要6秒呢? 按照upstream中定義的權重應該是平分請求的,最起碼應該有2秒的時候。 經過分析得知:最終返回給用戶請求的是web1,那麼當再次請求的時候一定會分給web2,由於web2是sleep 5秒的,因此經過proxy_read_timeout的時間(4s)後會跳到web1,結果還是web1返回的請求,所花時間就是nginx在web2等待的時間+web1執行的時間,以此類推下一次nginx自然的還會分給web2……。 如果有更多的後端web,則判斷下一個請求服務器可以看當前返回給最終用戶的是那台服務器,然後根據upstream中定義的順序向下查詢(權重一樣的情況)

結論:

(1)上面的三次測試分別將proxy_read_timeout的值設置為2s、3s、4s的情況進行的。 最終的測試結果也都在後面做了解釋與說明。 由於我關閉了後端服務器的健康檢查(max_fails=0)因此判斷後端服務器情況的唯一依據便是proxy_read_timeout參數,如果這個參數設置得過小,但後端程序的執行或多或少會超過這個時間的話,這種情況nginx的效率是非常低的。

(2)上面的測試都是後端服務器正常但執行超時的情況下nginx根據proxy_read_timeout和proxy_next_upstream的值來選擇下一個服務器,那如果我後端服務器直接報錯的情況呢? 可以想到如果報錯信息在proxy_next_upstream 中有定義的話nginx還會跳到下一台服務器。 否則直接將保存信息返回給nginx從而最終呈獻給用戶

情況2:打開後端服務器健康檢查,測試程序執行時間超過或等於proxy_read_timeout值或後端服務器直接報錯的情況

Nginx配置修改內容 server 192.168.108.163 max_fails = 1;
server 192.168.108.164 max_fails = 1;
proxy_next_upstream error timeout http_500 http_502 http_504
proxy_read_timeout 2s
後端web服務器
Web1 test.php Web2 test.php
<?php
header(‘RS:Web1′);
$t = 2;
sleep($t);
echo 『sleep {$t}s<br>』;
echo 『web-1<br>』;
?>
<?php
header(‘RS:Web2′);
header(‘http/1.1 500 Internal Server Error ‘);
#$t = 5;
#sleep($t);
echo 『sleep {$t}s<br>』;
echo 『web-2<br>』;
?>
備註:

開啟了後端服務器健康檢查
proxy_read_timeout 2s (下面會隨著測試變更)
Web1程序仍然sleep 2s
修改了Web2程序,讓他直接返回500錯誤

測試開始:

(1)連續測試三次結果如下:

curl -I -w %{time_total}:%{time_connect}:%{time_starttransfer} www.test.com/test.php
HTTP/1.1 504 Gateway Time-out
Server: nginx/0.7.65
Date: Tue, 18 May 2010 07:01:48 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 183
Connection: keep-alive

2.005:0.001:2.005

curl -I -w %{time_total}:%{time_connect}:%{time_starttransfer} www.test.com/test.php
HTTP/1.1 502 Bad Gateway
Server: nginx/0.7.65
Date: Tue, 18 May 2010 07:01:50 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 173
Connection: keep-alive

0.001:0.001:0.001

curl -I -w %{time_total}:%{time_connect}:%{time_starttransfer} www.test.com/test.php
HTTP/1.1 504 Gateway Time-out
Server: nginx/0.7.65
Date: Tue, 18 May 2010 07:01:57 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 183
Connection: keep-alive

2.005:0.001:2.005

說明:

第1次請求所用時間是2秒,web1執行超時,web2返回了500錯誤,upstream沒有更多的後端,因此nginx直接把504扔出來了,同時標記web2,web1不可用。 查看後端2台web服務器的訪問日誌,均有nginx代理的訪問記錄。
第2次請求時間很短,報502錯誤,說明沒有可用的後端服務器接受請求。 查看後端兩台web服務器訪問日誌,沒有任何變化,說明這兩台服務器被nginx標記為不可用,沒有把請求轉向後端,直接返回用戶502錯誤
第3次請求同第1次

(2)修改proxy_read_timeout 3s 連續訪問6次後結果以及2台web服務器的日誌情況
curl -I -w %{time_total}:%{time_connect}:%{time_starttransfer} www.test.com/test.php
HTTP/1.1 200 OK
Server: nginx/0.7.65
Date: Tue, 18 May 2010 07:30:15 GMT
Content-Type: text/html; charset=UTF-8
Connection: keep-alive
Vary: Accept-Encoding
X-Powered-By: PHP/5.1.6
RS: Web1

2.003:0.001:2.002

訪問日誌

Web1
[18/May/2010:15:30:00
[18/May/2010:15:30:03
[18/May/2010:15:30:05
[18/May/2010:15:30:08
[18/May/2010:15:30:11
[18/May/2010:15:30:13

Web2

[18/May/2010:15:30:00
[18/May/2010:15:30:11

說明:

由訪問日誌可知:
第1次請求是被分到web2上的,由於它返回了500錯誤,因此請求被轉到web1,並標記web2不可用。
第2次至第4次均將請求給了web1,第四次請求完畢後距第一請求已經過去了8秒。
第5次請求時已經是fail_timeout參數默認的10s也就是標記web2不可用的時間已經過去了,因此在第5次訪問實際上和第一次情況是一樣的。

結論:

(1proxy_next_upstream參數很有用,他可以避免很多錯誤
(2max_fails參數在繁忙的大型系統中建議設置為3,如果沒有幾個後端服務器的話保持默認即可。
(3proxy_read_timeout要根據自身程序而定,不要過大,也不要太小。 如果是php程序,請參照php.ini中的max_execution_time選項值。

原創文章,轉載請註明:   轉自http://salogs.com

文章分類: apache, 代理伺服器  | 相關標籤:  | 留下對這篇文章的想法
作者: 丫忠
• 星期五, 六月 04th, 2010

最近在做一些web服務的負載及壓力測試,剛好看到一篇文章有提供幾款Linux上運作的壓力測試工具,先記錄下來,有機會再來用看看,也提供給有需要的人作參考。

1. apache的ab工具
2. apache的flood工具 (http://httpd.apache.org/test/flood/)
3. web-bench工具 (http://freshmeat.net/projects/web-bench/)
4. http_load工具 (http://acme.com/software/http_load/)
5. siege工具 (http://www.joedog.org/index/siege-home)
siege有支援https

作者: 丫忠
• 星期二, 四月 20th, 2010

丫忠 今天終於花了點時間測試了一下中文網址對dns 及 apache這兩個伺服器的影響,講到 中文網址 的部份,丫忠以前的觀念是,誰會想輸入中文網址,除了要鍵入的鍵盤數比較多外,還要中英文輸入法切換…等不方便。

但是,這是站在丫忠的角度去想,如果對於剛接觸電腦或者對於網際網路不熟悉的使用者而言,他或許認為輸入【丫忠的伺服器.tw】或者輸入【xxx公司.tw】會比輸入英文網址來的好記;另外,以SEO角度而言,G神把中文網址部份加大權重,也並不是不可能的事。

所以囉,只要有需求就會有市場,只要有市場再不想去做的事也要做。既然做了就把它記錄下來給大家參考囉。

在講到dns及apache中文網址的觀念及設定以前,有一個很重要的觀念一定要很清楚,否則您會不知道丫忠在講什麼。這個就是 punycode,簡單一句就是將 中文網址 做轉碼的動作。或者您可以參考wikipedia的說法【punycode是一個根據RFC 3492標準而製定的編碼系統,主要用於把域名從地方語言所採用的Unicode編碼轉換成為可用於DNS系統的編碼。而該編碼是根據[1](由IANA制定),Punycode可以防止所謂的IDN欺騙。】,除了需要先了解 punycode外,你還必須有設定 dns(named.conf及zone)以及apache(虛擬主機)的經驗。

如果你的目的跟丫忠一樣,目的是讓 DNS 及 apache 支援中文網址的話,那麼有一個流程是丫忠試出來的結果,這觀念對於 dns 及 apache的設定上會有很大幫助。底下做個例子說明:

例子:當使用者從瀏覽器輸入【丫忠的伺服器.tw】時,在dns及apache伺服器是要如何接收這樣的中文網址呢?
流程如下:
1. 使用者從瀏覽器輸入【丫忠的伺服器.tw】
2. 瀏覽器會將【丫忠的伺服器.tw】轉換成 punycode (punycode 為 xn--diqrzk6o8qkq4iw8x.tw)
3. 依據此punycode(xn--diqrzk6o8qkq4iw8x.tw) 去 dns伺服器查詢IP
4. 當查詢到IP後,再將此punycode(xn--diqrzk6o8qkq4iw8x.tw)連至 apache伺服器

以上的流程中有一個很重要的地方,那就是 dns 及 apache都是依據 punycode 去做查詢IP 及 連結至網站,而不是依據 中文網址;所以,我們在做dns 及 apache設定時,都是要依據 punycode 而不是 中文網址

有了以上的概念後,dns及apache的設定就跟設定英文網址沒什麼差別,只是將英文網址改成punycode而已,講到這裡你可能會需要 punycode轉碼工具 以及 dns中文網址設定範例 以及 apache中文網址設定範例,丫忠就將這些工具整理如下:

1.  punycode轉碼工具 (本站的 punycode轉碼工具 或者 PHP可以藉由 idn_to_ascii 函式轉碼)
2. dns中文網址設定範例
3. apache中文網址設定範例:只需要修改 ServerName 即可
原本的vhost.conf設定
<VirtualHost *:80>
ServerName 丫忠的伺服器.tw
…..
</VirtualHost>

punycode轉碼後的設定
<VirtualHost *:80>
ServerName xn--diqrzk6o8qkq4iw8x.tw
…..
</VirtualHost>