原文鏈接:http://dinguangx.iteye.com/blog/1934994
經過前面的集羣實施,已經將Nginx+Tomcat的集羣環境給配置起來了,接着繼續進行集羣的故障轉移實驗.
這裏的故障轉移包括節點關閉情況和節點宕機情況的故障轉移.
首先對於節點關閉或加入的情況,比如某一Tomcat節點關閉或重啓的情況,在這種情況下,nginx可以快速識別到已停用或新加入的節點,基本上可以做到無延時的故障轉移.所以這裏主要實驗的是tomcat宕機的情況,比如tomcat運行過程中出現內存溢出或長時間不響應的情況.
爲了實驗的需要,在tomcat7080的啓動參數中增加內存的配置,設置其最大可用內存爲64m:
- JAVA_OPTS="-Xms32m -Xmx64m"
並在tomcat7080中,使用一段程序不斷地往內存中寫入數據,以使它出現內存溢出錯誤,不再處理新的訪問請求.
<body>
<%!
static HashMap<String,String> map = new HashMap<String,String>();
public String generateStr(){
StringBuilder s = new StringBuilder();
while(s.length()<50){
s.append(new Random().nextInt());
}
return s.toString();
}
%>
<%
int c = 500000;
try {
c=Integer.parseInt(request.getParameter("c"));
} catch(Exception e){
c = 500000;
}
for(int i = 0; i < c ;i++){
map.put(String.valueOf(1000000+i),generateStr());
}
%>
</body>
在tomcat7080啓動之後,訪問這段程序所在的jsp文件,tomcat很快便出現內存溢出錯誤,成功宕機.
此時通過程序來模擬一個單併發,每秒發出一次請求的客戶端:
public static void main(String[] args) {
for (int i = 0; i < 90; i++) {//測試90次
try {
doGet();
Thread.sleep(1000);
} catch (Exception e) {
// e.printStackTrace();
}
}
}
public static void doGet() throws Exception {
URL url = new URL("http://localhost/");
HttpURLConnection conn;
BufferedReader reader = null;
conn = (HttpURLConnection) url.openConnection();
String s;
int rc = conn.getResponseCode();
if (rc != 200) {
System.out.println("WARN: response code:" + rc);
}
reader = new BufferedReader(new InputStreamReader(
conn.getInputStream(), "UTF-8"), 512);
String line;
while ((line = reader.readLine()) != null) {
}
if (reader != null)
reader.close();
}
日誌文件中的輸出結果爲:
從日誌輸出中可以看到,nginx仍然會嘗試去請求已經宕機的7080端口,但在等待60秒之後將請求轉發給了6080,然後在大約13秒左右的時間內都只會請求6080端口,然後再去嘗試請求7080端口,依次循環.
要解釋出現這個現象的原因,需要來看一下集羣中server的參數以及proxy_connect_timeout, proxy_read_timeout等參數的設置
在nginx中,upstream中的server語法如下:
(參考http://nginx.org/en/docs/http/ngx_http_upstream_module.html)
server address [weight=number] [max_fails=number] [fail_timeout=time] [slow_start=time] [backup] [down];
其中max_fails和fail_timeout的默認值分別爲1和10s,這兩個參數配置起來使用.含義是:在fail_timeout的時間內,nignx與upstream中某個server的連接嘗試失敗了max_fails次,則nginx會認爲該server已經失效。在接下來的 fail_timeout時間內,nginx不再將請求分發給失效的server。所以在默認的情況下,nginx在前一次嘗試連接7080端口失敗後,在10秒之後纔會再次去嘗試(這裏的實際是大約是13秒,考慮請求轉發的原因,基本上可以認爲是一個正常值).
然後是location中的proxy_connect_timeout和proxy_read_timeout設置.這兩個參數的含義如下:
proxy_connect_timeout
後端服務器連接的超時時間_發起握手等候響應超時時間(默認爲60s,不建議超過75s)
proxy_read_timeout
連接成功後_等候後端服務器響應時間_其實已經進入後端的排隊之中等候處理(也可以說是後端服務器處理請求的時間)(默認爲60s)
分析我們前面的實驗,tomcat7080在內存溢出的情況下,仍然能夠與nginx完成握手,但是卻不能處理結果,所以等待的一分鐘時間是耗費在proxy_read_timeout了.如果能設置一個合適的值,就可以在可接受的時間範圍內,完成集羣的故障遷移.
在測試過程中,最終的故障遷移時間配置如下:
即可承受的請求響應時間爲5s,在故障被檢測到之後,1m內不再向故障節點發起新請求.(實際生產環境中可按需要適當進行調整)