死磕nginx系列--使用nginx作負載均衡

使用nginx作負載均衡的兩大模塊:html

  • upstream 定義負載節點池。
  • location 模塊 進行URL匹配。
  • proxy模塊 發送請求給upstream定義的節點池。

upstream模塊解讀

nginx 的負載均衡功能依賴於 ngx_http_upstream_module模塊,所支持的代理方式有 proxy_pass(通常用於反向代理),fastcgi_pass(通常用於和動態程序交互),memcached_pass,proxy_next_upstream,fastcgi_next_pass,memcached_next_pass 。前端

upstream 模塊應該放於http{}標籤內。android

模塊寫法:nginx

upstream backend {
    ip_hash; 
    server backend1.example.com       weight=5;
    server backend2.example.com:8080;
    server backup1.example.com:8080   backup;
    server backup2.example.com:8080   backup;
}

實例一:正則表達式

upstream dynamic {
    zone upstream_dynamic 64k;

    server backend1.example.com      weight=5;
    server backend2.example.com:8080 fail_timeout=5s slow_start=30s;
    server 192.0.2.1                 max_fails=3;
    server backend3.example.com      resolve;

    server backup1.example.com:8080  backup;
    server backup2.example.com:8080  backup;
}

語法解釋:算法

nginx默認支持四種調度算法

  • 輪詢(rr),每一個請求按時間順序逐一分配到不一樣的後端服務器,若是後端服務器故障,故障系統自動清除,使用戶訪問不受影響。
  • 輪詢權值(weight),weight值越大,分配到的訪問概率越高,主要用於後端每一個服務器性能不均的狀況。
  • ip_hash,每一個請求按訪問IP的hash結果分配,這樣來自同一個IP的固定訪問一個後端服務器,主要解決動態網站session共享的問題。
  • url_hash,按照訪問的URL的hash結果來分配請求,是每一個URL定向到同一個後端服務器,能夠進一步提升後端緩存服務器的效率,nginx自己不支持,若是想使用須要安裝nginx的hash軟件包。
  • fair,這個算法能夠依據頁面大小和加載時間長短智能的進行負載均衡,也就是根據後端服務器的響應時間來分配請求,相應時間短的優先分配,默認不支持,若是想使用須要安裝upstream_fail模塊。
  • least_conn 最少連接數,那個機器鏈接數少就分發。

server模塊的寫法

server IP 調度狀態shell

server指令指定後端服務器IP地址和端口,同時還能夠設定每一個後端服務器在負載均衡調度中的狀態。apache

  • down 表示當前的server暫時不參與負載均衡。
  • backup 預留的備份服務器,當其餘全部的非backup服務器出現故障或者忙的時候,纔會請求backup機器,由於這臺集羣的壓力最小。
  • max_fails 容許請求失敗的次數,默認是1,當超過最大次數時,返回proxy_next_upstream模塊定義的錯誤。0表示禁止失敗嘗試,企業場景:2-3.京東1次,藍汛10次,根據業務需求去配置。
  • fail_timeout,在經歷了max_fails次失敗後,暫停服務的時間。京東是3s,藍汛是3s,根據業務需求配置。常規業務2-3秒合理。

例:若是max_fails是5,他就檢測5次,若是五次都是502.那麼,他就會根據fail_timeout 的值,等待10秒,再去檢測。後端

server 若是接域名,須要內網有DNS服務器,或者在負載均衡器的hosts文件作域名解析。server後面還能夠直接接IP或IP加端口。api

長鏈接 keepalive

upstream backend {
    server backend2.example.com:8080;
    server backup1.example.com:8080   backup;
    keepalive 100;
}

經過該指令配置了每一個worker進程與上游服務器可緩存的空閒鏈接的最大數量。
當超出這個數量時,最近最少使用的鏈接將被關閉。keepalive指令不限制worker進程與上游服務器的總鏈接。

location / {
    # 支持keep-alive
    proxy_http_version 1.1;
    proxy_set_header Connection "";
    proxy_pass http://backup;
}
  • 若是是http/1.0 須要配置發送"Connection: Keep-Alive" 請求頭。
  • 上游服務器不要忘記開啓長鏈接支持。

鏈接池配置建議

  • 總長鏈接數是"空閒鏈接池"+"釋放鏈接池"的長鏈接總數。
  • 首先,長鏈接配置不會限制worker進程能夠打開的總鏈接數(超了的做爲短鏈接)。另外鏈接池必定要根據場景合理進行設置。
  1. 空閒鏈接池過小,鏈接不夠用,須要不斷建鏈接。
  2. 空閒鏈接池太大,空閒鏈接太多,還沒使用就超時。
  3. 建議只對小報文開啓長鏈接。

location 模塊解讀

location做用:基於一個指令設置URI。
基本語法:

Syntax: location [ = | ~ | ~* | ^~ ] uri { ... }
location @name { ... }
Default:    —
Context:    server, location
  • = 精確匹配,若是找到匹配=號的內容,當即中止搜索,並當即處理請求(優先級最高)
  • ~ 區分大小寫
  • ~* 不區分大小寫
  • ^~ 只匹配字符串,不匹配正則表達式
  • @ 指定一個命名的location,通常用於內部重定義請求,location @name {…}

匹配是有優先級的,不是按照nginx的配置文件進行。

官方的例子:

location = / {
    [ configuration A ]
}
location / {
    [ configuration B ]
}
location /documents/ {
    [ configuration C ]
}
location ^~ /images/ {
    [ configuration D ]
}
location ~* \.(gif|jpg|jpeg)$ {
    [ configuration E ]
}

結論:

  • / 匹配A。
  • /index.html 匹配B
  • /documents/document.html 匹配C
  • /images/1.gif 匹配D
  • /documents/1.jpg 匹配的是E。

測試用的例子:

location / {
           return 401;
        }
        location = / {
            return 402;
        }
        location /documents/ {
            return 403;
        }
        location ^~ /images/ {
            return 404;
        }
        location ~* \.(gif|jpg|jpeg)$ {
            return 500;
        }

測試結果(重點看):

[root@lb01 conf]# curl -I -s -o /dev/null -w "%{http_code}\n" http://10.0.0.7/
402
[root@lb01 conf]# curl -I -s -o /dev/null -w "%{http_code}\n" http://10.0.0.7/index.html
401
[root@lb01 conf]# curl -I -s -o /dev/null -w "%{http_code}\n" http://10.0.0.7/documents/document.html 
403
[root@lb01 conf]# curl -I -s -o /dev/null -w "%{http_code}\n" http://10.0.0.7/images/1.gif
404
[root@lb01 conf]# curl -I -s -o /dev/null -w "%{http_code}\n" http://10.0.0.7/dddd/1.gif  
500

結果總結:
匹配的優先順序,=>^~(匹配固定字符串,忽略正則)>徹底相等>~*>>/
工做中儘可能將'='放在前面

proxy_pass 模塊解讀

proxy_pass 指令屬於ngx_http_proxy_module 模塊,此模塊能夠將請求轉發到另外一臺服務器。

寫法:

proxy_pass http://localhost:8000/uri/;

實例一:

upstream blog_real_servers {
         server 10.0.0.9:80  weight=5;
         server 10.0.0.10:80  weight=10;
         server 10.0.0.19:82  weight=15;
    }
    server {
       listen       80;
       server_name  blog.etiantian.org;
       location / {
        proxy_pass http://blog_real_servers;
       proxy_set_header host $host;
       }
    }
  • proxy_set_header:當後端Web服務器上也配置有多個虛擬主機時,須要用該Header來區分反向代理哪一個主機名,proxy_set_header host $host;
  • proxy_set_header X-Forwarded-For :若是後端Web服務器上的程序須要獲取用戶IP,從該Header頭獲取。proxy_set_header X-Forwarded-For $remote_addr;

配置後端服務器接收前端真實IP

配置以下:

log_format  commonlog  '$remote_addr - $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_x_forwarded_for"';

rs_apache節點的httpd.conf配置

LogFormat "\"%{X-Forwarded-For}i\" %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{U
ser-Agent}i\"" combined修改日誌記錄
apache
LogFormat "\"%{X-Forwarded-For}i\" %l %u %t \"%r\" %>s %b" common

proxy_pass相關的優化參數

  • client_max_body_size 10m; 容許客戶端請求的最大的單個文件字節數。
  • client_body_buffer_size 128k; 緩衝區代理緩衝用戶端請求的最大字節數 能夠理解爲先保存到本地再傳給用戶。
  • proxy_connect_timeout 600; 跟後端服務器鏈接的超時時間_發起握手等候響應超時時間。
  • proxy_read_timeout 600; 鏈接成功後_等候後端服務器響應時間_其實已經進入後端的排隊之中等候處理。
  • proxy_send_timeout 600; 後端服務器回傳數據時間,就是在規定時間以內後端服務器必須傳完全部的數據。
  • proxy_buffer_size 8k; 代理請求緩存區,這個緩存區間會保存用戶的頭信息以供Nginx進行規則處理,通常只要設置能保存下頭信息便可。
  • proxy_buffers 4 32k; 同上 告訴Nginx保存單個頁面使用的空間大小,假設網頁大小平均在32k如下的話。
  • proxy_busy_buffers_size 64k; 若是系統很忙的時候能夠申請更大的proxy_buffers 官方推薦(proxy_buffers*2)。
  • proxy_max_temp_file_size 1024m; 當 proxy_buffers 放不下後端服務器的響應內容時,會將一部分保存到硬盤的臨時文件中,這個值用來設置最大臨時文件大小,默認1024M,它與 proxy_cache 沒有關係。大於這個值,將從upstream服務器傳回。設置爲0禁用。
  • proxy_temp_file_write_size 64k; proxy緩存臨時文件的大小 proxy_temp_path(能夠在編譯的時候)指定寫到哪那個目錄。

健康檢查

Nginx提供了health_check語句來提供負載(upstream)時的鍵康檢查機制(注意:此語句須要設置在location上下文中)。

支持的參數有:

  • interval=time:設置兩次健康檢查之間的間隔值,默認爲5秒
  • fails=number:設置將服務器視爲不健康的連續檢查次數,默認爲1次
  • passes=number:設置一個服務器被視爲健康的連續檢查次數,默認爲1次
  • uri=uri:定義健康檢查的請求URI,默認爲」/「
  • match=name:指定匹配配置塊的名字,用記測試響應是否經過健康檢測。默認爲測試返回狀態碼爲2xx和3xx

一個簡單的設置以下,將使用默認值:

location / {
    proxy_pass http://backend;
    health_check;
}

對就應用,咱們能夠專門定義一個API用於健康檢查:/api/health_check,並只返回HTTP狀態碼爲200。並設置兩次檢查之間的間隔值爲1秒。這樣,health_check語句的配置以下:

health_check uri="/api/health_check" interval;

匹配match的方法

http {
    server {
    ...
        location / {
            proxy_pass http://backend;
            health_check match=welcome;
        }
    }

    match welcome {
        status 200;
        header Content-Type = text/html;
        body ~ "Welcome to nginx!";
    }
}

match 例子舉例

  • status 200;: status 等於 200
  • status ! 500;: status 不是 500
  • status 200 204;: status 是 200 或 204
  • status ! 301 302;: status 不是301或302。
  • status 200-399;: status 在 200 到 399之間。
  • status ! 400-599;: status 不在 400 到 599之間。
  • status 301-303 307;: status 是 301, 302, 303, 或 307。
  • header Content-Type = text/html;: 「Content-Type」 得值是 text/html。
  • header Content-Type != text/html;: 「Content-Type」 不是 text/html。
  • header Connection ~ close;: 「Connection」 包含 close。
  • header Connection !~ close;: 「Connection」 不包含 close。
  • header Host;: 請求頭包含 「Host」。
  • header ! X-Accel-Redirect;: 請求頭不包含 「X-Accel-Redirect」。
  • body ~ "Welcome to nginx!";: body 包含 「Welcome to nginx!」。
  • body !~ "Welcome to nginx!";: body 不包含 「Welcome to nginx!」。

一個完整的nginx實例

[root@lb01 conf]# cat nginx.conf
worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    #blog lb by oldboy at 201303
    upstream blog_real_servers {
   server   10.0.0.9:80 weight=1 max_fails=1 fail_timeout=10s;
   server   10.0.0.10:80 weight=1 max_fails=2 fail_timeout=20s;

    }
    server {
       listen       80;
       server_name  blog.etiantian.org;
       location / {
        proxy_pass http://blog_real_servers;
        include proxy.conf;
       }
    }
}
[root@lb01 conf]# cat proxy.conf 
        proxy_set_header Host $host;
        proxy_set_header X-Forwarded-For $remote_addr;
        proxy_connect_timeout 90;        
        proxy_send_timeout 90;
        proxy_read_timeout 90;
        proxy_buffer_size 4k;
        proxy_buffers 4 32k;
        proxy_busy_buffers_size 64k; proxy_temp_file_write_size 64k;

擴展補充

只容許使用GET,HEAD,POST方法去請求

## Only allow these request methods ##
     if ($request_method !~ ^(GET|HEAD|POST)$ ) {
         return 444;
     }

實戰

根據URI及location實現動靜分離。

最終實現:

  1. /static/的URL都去訪問10.0.0.9。
  2. /dynamic/的URL都去訪問10.0.0.10。
  3. 圖片這些靜態文件去訪問10.0.0.9。
  4. /upload/的URL都去訪問10.0.0.10。
[root@lb01 conf]# cat nginx.conf
worker_processes  1;
events {
    worker_connections  1024;
}
http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    #blog lb by oldboy at 201303

    upstream static_pools {
      server 10.0.0.9:80;
    }
    upstream dynamic_pools {
      server 10.0.0.10:80;
    }
     upstream upload_pools {
      server 10.0.0.9:80;
    }

    server {
       listen       80;
       server_name  blog.biglittleant.cn;
       
        location / {
        proxy_pass http://static_pools;
        include proxy.conf;
       }

       location /static/ { 
        proxy_pass http://static_pools;
        include proxy.conf;
       }
       
        location ~* \.(gif|jpg|jpeg)$ {
         proxy_pass http://static_pools;
         include proxy.conf;
        }

       location /dynamic/ { 
        proxy_pass http://dynamic_pools;
        include proxy.conf;
       }
       location /upload/ {
        proxy_pass http://upload_pools;
        include proxy.conf;
       }
    }
}

實現蘋果手機和安卓手機訪問不一樣的地址

server {
       listen       80;
       server_name  blog.etiantian.org;
       location / {
        if ($http_user_agent ~* "android")
          {
            proxy_pass http://android_pools;
          }
        if ($http_user_agent ~* "iphone")
          {
            proxy_pass http://iphone_pools;
            }
        proxy_pass http://pc_pools;
        include extra/proxy.conf;
       }
        access_log off;
     }

參考文檔

nginx-proxy_pass官網

相關文章
相關標籤/搜索