Nginx+Tomcat集羣的故障遷移實驗

原文鏈接:http://dinguangx.iteye.com/blog/1934994

        經過前面的集羣實施,已經將Nginx+Tomcat的集羣環境給配置起來了,接着繼續進行集羣的故障轉移實驗.

  這裏的故障轉移包括節點關閉情況和節點宕機情況的故障轉移.

  首先對於節點關閉或加入的情況,比如某一Tomcat節點關閉或重啓的情況,在這種情況下,nginx可以快速識別到已停用或新加入的節點,基本上可以做到無延時的故障轉移.所以這裏主要實驗的是tomcat宕機的情況,比如tomcat運行過程中出現內存溢出或長時間不響應的情況.

  爲了實驗的需要,在tomcat7080的啓動參數中增加內存的配置,設置其最大可用內存爲64m:

  

Java代碼  收藏代碼
  1. JAVA_OPTS="-Xms32m -Xmx64m"  

 

  並在tomcat7080中,使用一段程序不斷地往內存中寫入數據,以使它出現內存溢出錯誤,不再處理新的訪問請求.

Java代碼  收藏代碼

 

<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很快便出現內存溢出錯誤,成功宕機.

  

  此時通過程序來模擬一個單併發,每秒發出一次請求的客戶端:

Java代碼  收藏代碼

 

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)

Java代碼  收藏代碼

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了.如果能設置一個合適的值,就可以在可接受的時間範圍內,完成集羣的故障遷移.

  在測試過程中,最終的故障遷移時間配置如下:

 

   

Java代碼  收藏代碼

 upstream cluster {  
        server localhost:6080 weight=10 fail_timeout=1m;  
        server localhost:7080 weight=10 fail_timeout=1m;  
  }  
    
    
location / {  
  proxy_pass http://cluster;  
  proxy_set_header Host $host;  
     proxy_set_header X-Real-IP $remote_addr;  
  proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
  proxy_connect_timeout       2s;  
  proxy_read_timeout          5s;  
  proxy_send_timeout          5s;  
}  



 即可承受的請求響應時間爲5s,在故障被檢測到之後,1m內不再向故障節點發起新請求.(實際生產環境中可按需要適當進行調整)

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