1. 程式人生 > >nginx的反向代理功能和緩存功能

nginx的反向代理功能和緩存功能

inpu port dev ria 最全 number con ref 方便

本文目錄:
1. nginx的反向代理功能
 1.1 正向代理和反向代理
 1.2 配置簡單的反代實驗
 1.3 使用upstream模塊實現分組反向代理
 1.4 ngx_http_proxy_module模塊
  1.4.1 指令及其意義
  1.4.2 proxy_pass
  1.4.3 proxy_set_header
 1.5 ngx_http_upstream_module模塊
 1.6 反向代理的各種情況
 1.7 nginx代理memcached
2. nginx自帶的緩存功能

1. nginx的反向代理功能

1.1 正向代理和反向代理

正向代理是眾多內網客戶機上網訪問互聯網上的網站時,將所有的請求交給內網前面處於公網上的"管家"服務器,由"管家"服務器代為請求想要訪問的web服務器,然後將得到的結果緩存下來並提供給客戶端,這是正向代理。"管家"服務器稱為正向代理服務器。

技術分享

反向代理是客戶端訪問web服務器時,請求發送到真實的web服務器的前端"助手"服務器上,由"助手"服務器決定將此請求轉發給哪個真實的web服務器,外界客戶端以為"助手"服務器就是真實的web服務器,而實際上它不是,也不需要安裝任何web程序。"助手"服務器稱為反向代理服務器。

技術分享

nginx是一個優秀的反向代理服務程序,通過反向代理可以實現負載均衡的效果。因為是通過反向代理實現的負載均衡,所以nginx實現的是七層負載均衡。它能夠識別http協議,根據http報文將不同類型的請求轉發到不同的後端web服務器上。後端的web服務器稱為"上遊服務器",即upstream服務器。

實際上,nginx和php-fpm結合的時候,指令fastcgi_pass實現的也是反向代理的功能,只不過這種代理功能是特定的功能,只能轉發給php-fpm。

nginx的反向代理有幾種實現方式:

  1. 僅使用模塊ngx_http_proxy_module實現簡單的反向代理。指令為proxy_pass。
  2. 使用fastcgi模塊提供的功能,反向代理動態內容。指令為fastcgi_pass。
  3. 使用ngx_http_memcached_module模塊提供的功能,反向代理memcached緩存內容,指令為memcached_pass。
  4. 結合upstream模塊實現更人性化的分組反向代理。

1.2 配置簡單的反代實驗

實驗環境如下圖:

技術分享

反向代理服務器nginx-proxy(192.168.100.29)的配置。由於是做代理,所以配置文件的location段不再需要root、index等指令,只需幾個和代理相關的指令即可。

server {
    listen       80;
    server_name  www.longshuai.com;
    location ~ \.(png|jpg|jpeg|bmp|gif)$ {
        proxy_pass http://192.168.100.28:80;
    }
    location / {
        proxy_pass http://192.168.100.30:80/;
    }
    location ~ \.(php|php5)$ {
        proxy_pass http://192.168.100.25:80;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

提供動態服務的nginx服務器(192.168.100.25)的配置如下。

server {
    listen       80;
    server_name  www.longshuai.com;
    location / {
        root    /www/longshuai/;
        index   index.php index.html index.htm;
    }
    location ~ \.php$ {
        root /php/;
        fastcgi_pass    192.168.100.27:9000;
        fastcgi_index   test.php;
        include         fastcgi.conf;
    }
}

其中php-fpm服務器(192.168.100.27)上的/www/longshuai/index.php內容如下:

<h1>page from php-fpm</h1>
<?php
phpinfo();
?>

LB1(192.168.100.28)和LB2(192.168.100.30)的web程序都是httpd。其中作為一般靜態web服務器的LB2的配置文件沒有任何修改,它的/var/www/html/index.html的內容如下:

<h1>LB2:static</h1>

作為圖片服務器的LB1在配置文件中添加了如下幾行。且其/var/www/html/下有一個圖片文件a.png。

<Files ~ "\.(png|jpeg|jpg|bmp|gif)">
        Order allow,deny
        Allow from all
</Files>

經過以上的配置,可以實現如下圖的功能。當訪問www.longshuai.com的時候,任意以php結尾的文件請求都轉發給nginx再由nginx交由php-fpm處理;任意以圖片格式結尾(png/jpg/jpeg/bmp/gif)的請求都轉發給LB1;任意非以上兩種格式的請求都轉發給LB2。

技術分享

重載nginx-proxy/nginx/LB1/LB2上的nginx或者httpd。然後進行測試。

技術分享 技術分享

1.3 使用upstream模塊實現分組反向代理

前面只使用了ngx_http_proxy_module來實現反向代理,但是其缺陷在於在nginx-proxy上定義的每條代理規則都只能轉發到後臺的某一臺服務器上,即後端服務器無法分組。例如當圖片服務器壓力太大,添加一臺服務器想要減輕圖片服務器壓力,但是僅使用proxy模塊無法實現此類負載均衡到多臺圖片服務器上。

這時需要借助ngx_http_upstream_module模塊,該模塊用於定義後端服務器池,後端服務器也稱為上遊服務器(upstream),可以為每一種類型的後端服務器分一個組。然後在結合proxy_pass或其他代理指令將相應的請求轉發到池內。

服務器池可以有多臺服務器,多臺服務器如何實現負載均衡和算法有關,默認是指定權重的加權均衡算法,還可以指定ip_hash算法實現同一個客戶端IP發起的請求總是轉發到同一臺服務器上。還有一些其它算法,如最小連接數算法。最常用的還是加權算法,然後通過session共享的方式實現同一個客戶端IP發起的請求轉發到同一服務器上。

例如,下圖描述的需求。當請求圖片服務器時,可以將請求均衡到IP3和IP4兩臺服務器上,當請求其他靜態內容,可以將請求均衡到IP5和IP6兩臺服務器上。

技術分享

要實現這樣的功能,nginx-proxy上的nginx配置文件內容大致如下:

http {
    include mime.types;
    default_type application/octet-stream;
    sendfile on;
    keepalive_timeout 65;

    # define server pool
    upstream dynamic_pool {
        server IP1:80;
    }
    upstream pic_pool {
        server IP3:80 weight=2;
        server IP4:80 weight=1;
    }
    upstream static_pool {
        server IP5:80 weight=1;
        server IP6:80 weight=1;
    }

    server {
        listen 80;
        server_name www.longshuai.com;

        # define how to proxy
        location ~ \.(php|php5)$ {
            proxy_pass http://dynamic_pool;
        }
        location ~ \.(png|jpeg|jpg|bmp|gif)$ {
            proxy_pass http://pic_pool;
        }
        location / {
            proxy_pass http://static_pool;
        }
    }
}

1.4 ngx_http_proxy_module模塊

1.4.1 指令及其意義

該模塊默認安裝。以下是相關指令的說明。

指令指令意義
proxy_pass 定義代理到哪臺服務器或哪個upstream池
proxy_set_header 在代理服務器上設置http報頭信息。如加上真實客戶端地址"proxy_set_header X_Forwarded_For $remote_addr"
proxy_connect_timeout 反向代理連接上遊服務器節點的超時時間。發起方是proxy方,即等待握手成功的時間
proxy_send_timeout 上遊服務器節點數據傳給代理服務器的超時時間。即此時間段內,後端節點需要傳完數據給代理服務器
proxy_read_timeout 定義代理服務器何時關閉和後端服務器連接的超時時長,默認為60秒,表示某次後端傳輸完數據給代理服務器後,如果60秒內代理服務器和後端服務器沒有任何傳輸,則關閉此次連接。目的是避免代理服務器和後端服務器一直保持連接不斷開而占用資源

1.4.2 proxy_pass

proxy_pass http[s]://{ [IP:PORT/uri/] | upstream_pool };

該指令在前文示例中已經演示過了。此處只說明註意點。

當proxy_pass所在的location中使用了正則匹配時,則proxy_pass(包括fastcgi_pass和memcached_pass)定義的轉發路徑都不能包含任何URI信息。另外,location中使用了尾隨斜線,那麽proxy_pass定義的轉發路徑也必須使用斜線,或者都不加尾隨斜線。

例如下面的配置方式是允許的。當訪問www.longshuai.com/forum/時將被轉發到http://192.168.100.25:8080/bbs/上。

server_name www.longshuai.com;
location /forum/ {
    proxy_pass http://192.168.100.25:8080/bbs/;
}

而如果使用了正則匹配,將是不允許的。如下。

server_name www.longshuai.com;
location ~ ^/forum/ {
    proxy_pass http://192.168.100.25:8080/bbs/;
}

只能修改轉發路徑使其不包含URI。如下。此時請求www.longshuai.com/forum/將轉發到http://192.168.100.25:8080/forum/。

server_name www.longshuai.com;
location ~ ^/forum/ {
    proxy_pass http://192.168.100.25:8080/;
}

1.4.3 proxy_set_header

可以在nginx配置文件中的http段、server段或location段設置proxy_set_header指令。設置該指令後,傳輸給上遊服務器的報文首部將包含相關的信息,如設置客戶端的真實IP地址。設置如下:

server {
    listen       80;
    server_name  www.longshuai.com;
    location ~ \.(png|jpg|jpeg|bmp|gif)$ {
        proxy_pass http://192.168.100.28:80;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
    location / {
        proxy_pass http://192.168.100.30:80/;
        proxy_set_header X-Forwarded-For $remote_addr;

    }
    location ~ \.(php|php5)$ {
        proxy_pass http://192.168.100.25:80;
        proxy_set_header X-Forwarded-For $remote_addr;
    }
    error_page   500 502 503 504  /50x.html;
    location = /50x.html {
        root   html;
    }
}

僅在代理服務器上設置了該頭部字段後還不夠,因為後端服務器僅僅只是獲取到它,默認並沒有將其記錄下來。所以需要在後端的服務的日誌格式設置上記錄此項或者在其他有需求的地方設置它。nginx的日誌格式和apache的日誌設置格式不同,以下分別是兩種web程序的設置方法:

# nginx上的日誌設置格式
log_format  main  $remote_addr - $remote_user [$time_local] "$request" ‘
              $status $body_bytes_sent "$http_referer" ‘
              ‘"$http_user_agent" "$http_x_forwarded_for" ‘;
access_log logs/access.log main;

# apache上的日誌設置格式
LogFormat "%{X-Forwarded-For}i %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined

以下是nginx日誌上記錄的信息。

[root@xuexi nginx]# tail -1 logs/access.log 
192.168.100.29 - - [26/Apr/2017:14:35:30 +0800] "GET /index.php HTTP/1.0" 200 76990 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36" "192.168.100.1"

以下是apache日誌中記錄的信息。

[root@xuexi ~]# tail -1 /etc/httpd/logs/access_log
192.168.100.1 192.168.100.29 - - [26/Apr/2017:14:32:52 +0800] "GET /a.png HTTP/1.0" 200 2653 "-" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.98 Safari/537.36"

1.5 ngx_http_upstream_module模塊

upstream模塊定義上遊服務器組。主要的指令有"upstream"、"server"、"ip_hash"。upstream指令必須定義在server段外面。

以下是一個綜合示例定義方法,並非正確,只是放在一起方便比較用法。

upstream backend {
    server 192.168.100.25;
    server 192.168.100.26:80;
    server www.longshuai.com;
    server www.xiaofang.com:8080;
    server 192.168.100.27 weight=2 max_fails=2 fail_timeout=2s;
    server 192.168.100.28 down;
    server 192.168.100.29 backup;
    ip_hash;   # 定義此項後,前面的server附加項weight和backup功能失效。
}

默認使用加權均衡算法,使用ip_hash指令可設置為ip_hash算法,但使用ip_hash指令後,如果server指令中使用了weight和backup選項,這兩個功能將會失效。

其中server指令後可以跟的附加選項有:

  • weight:定義該後端服務器在組中的權重,默認為1,權重越大,代理轉發到此服務器次數越多。
  • max_fails和fail_timeout:定義代理服務器聯系後端服務器的失敗重聯系次數和失敗後重試等待時間。默認為1和10,如果設置為2和3,則表示只嘗試聯系2次,每次失敗等待3秒後進行下一次重試,也就是說6秒後就能判定此後端服務器無法聯系上,將負載均衡到下一臺服務器上。常會配合proxy_next_upstream或者fastcgi_next_upstream或者memcached_next_upstream來指定失敗時如何處理。
  • down:將此後端服務器定義為離線狀態。此功能不同於直接在配置文件中刪除此服務器配置或註釋它。常用於ip_hash均衡算法。當使用ip_hash算法時,如果某臺服務器壞了需要將其從服務器組中排除,直接從配置文件中直接刪除該服務器將會導致此前分配到此後端服務器的客戶端重新計算IP_HASH值,而使用down則不會。
  • backup:指定當其他非backup的server都聯系不上時,將返回backup所定義的服務器內容。常用於顯示sorry page。

當server指定後端服務器時使用的是主機名的方式時,需要在代理服務器上添加域名解析記錄,如放到/etc/hosts中。

以下是一個配置示例。只定義了一個upstream組,所有請求都代理,權重為2比1,當192.168.100.28和192.168.100.30都斷開聯系時,將返回代理服務器本身配置的sorrypage。

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

    upstream web_group {
        server 192.168.100.28 weight=2 max_fails=2 fail_timeout=2s;
        server 192.168.100.30 weight=1 max_fails=2 fail_timeout=2s;
        server 127.0.0.1:80 backup;
    }
    server {
        listen 127.0.0.1:80;
        root /www/longshuai/;
        index index.html;
    }
    server {
        listen       80;
        server_name  www.longshuai.com;
        location / {
                proxy_pass http://web_group;
        }
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
                root   html;
        }
    }
}

然後在反向代理服務器上創建/www/longshuai/目錄,並向目錄下的index.html文件中寫入"sorry...."。

mkdir -p /www/longshuai/
echo "<h1>sorry pages...</h1>" >/www/longshuai/index.html

重載代理服務器。在瀏覽器上輸入www.longshuai.com並不斷刷新,結果應該是2:1的返回權重。再依次測試停掉某一臺後端服務器和兩臺後端都停掉的情況。

1.6 反向代理的各種情況

反向代理時,可以根據uri的後綴來代理,也可以根據uri中的目錄來代理,還可以根據客戶端瀏覽器類型來代理。例如手機訪問網站時轉發到某個後端服務器組,IE瀏覽器訪問的轉發到某一個後端服務器組等。

# uri後綴代理。
location ~ \.(jpeg|jpg|png|bmp|gif)$ {
    proxy_pass ...
}

# 目錄代理。
location ~ /forum/ {
    proxy_pass ...
}

# 瀏覽器類型代理。
location / {
    if ($http_user_agent ~ "MSIE") {
        proxy_pass...
    }
    if ($http_user_agent ~ "Chrome") {
        proxy_pass...
    }
}

1.7 nginx代理memcached

該模塊可以從將請求代理至memcached server上,並立即從server上獲取響應數據。例如:

location / {
                set $memcached_key "$uri?$args";
                memcached_pass     127.0.0.1:11211;
}

nginx代理memcached時,需要以變量$memcached_key作為key去訪問memcached server上的數據。例如此處將$uri$args變量賦值給$memcached_key變量作為key去訪問memcached服務器上對應的數據。

但這樣的代理顯然不符合真正的需求:沒有實現memcached的分布式功能。當memcached server宕機時,nginx將無法從中獲取任何數據。所以應該使用上遊服務器組。例如:

upstream memcached {
    server 127.0.0.1:11211;
    server 127.0.0.1:11212;
    server 127.0.0.1:11213;
    server 127.0.0.1:11214;
}

server {
    listen       80;
    server_name  dev.hwtrip.com;

    location ^~ /cache/ {
        set            $memcached_key "$uri$args";
        memcached_pass memcached;

但這也不適合,因為memcached是基於一致性哈希算法的,而upstream模塊默認並不支持一致性哈希算法。可以通過upstream模塊的hash指令或者另外使用一個第三方模塊ngx_http_upstream_consistent_hash

如果使用的是upstream模塊的hash指令,配置如下:

upstream memcached {
    hash "$uri$args" consistent;
    server 127.0.0.1:11211;
    server 127.0.0.1:11212;
    server 127.0.0.1:11213;
    server 127.0.0.1:11214;
}

這樣,各上遊主機就通過hash一致性的算法進行負載均衡。

如果使用的是第三方模塊ngx_http_upstream_consistent_hash,則在模塊添加成功後如下配置upstream組:

upstream memcached {
    consistent_hash consistent;
    server 127.0.0.1:11211;
    server 127.0.0.1:11212;
    server 127.0.0.1:11213;
    server 127.0.0.1:11214;
}

2 nginx自帶的緩存功能

nginx的ngx_http_proxy_module自帶了緩存功能。有幾種緩存:網頁內容緩存,日誌緩存,打開文件緩存,fastcgi緩存。fastcgi緩存功能應慎用,因為動態程序的前後邏輯可能改變了,緩存後的結果可能並非實際所需結果。

在說明nginx自帶的緩存功能之前,需說明其缺陷。nginx的緩存功能主要用於緩存體積較小的頁面資源,當數據較大時很容易出現瓶頸。在頁面資源的緩存功能上,它屬於業余玩家。而squid是科班出身,功能最全面,可以緩存大量數據,但架構太老,性能一般。varnish則是此類緩存的新貴,架構較新,內存管理完全交由操作系統內核,性能是squid的幾倍之強,但緩存的內容不足squid

本節所講的主要是nginx自帶緩存的網頁內容緩存。當實現網頁內容緩存時,作為web服務程序,它可以緩存自身返回給客戶端的數據,包括讀取的圖片、文件等;作為代理,它可以緩存來自後端的數據緩存後的數據在內存中有,也會放在設定的目錄下。這樣以後客戶端繼續請求相同資源時,可以直接從內存中或者自身的磁盤中獲取並返回給客戶端。當緩存超出指定的空間大小時,將會有一個專門的線程cache_manager來清理緩存。

定義的相關指令主要有3個:proxy_cache_path、proxy_cache、proxy_cache_valid。

  • proxy_cache_path:它的語法比較復雜,但用起來很簡單。
    proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
    

其中proxy_cache_path path [levels=levels] keys_zone=name:size [max_size=size]這幾項是一般使用的選項和必需項。以下為一示例。

proxy_cache_path /usr/local/nginx/cache_dir levels=1:2 keys_zone=cache_one:20m max_size=1g;

其中:

  1. path:定義緩存放在磁盤的哪個目錄下。此處表示定義在/usr/local/nginx/cache_dir目錄下。目錄不存在會自動創建。
  2. levels:定義緩存目錄的級別,同時定義緩存目錄名稱的字符數。例如levels=1:2:2表示3級目錄,且第一級目錄名1個字符,第二級目錄2個字符,第三級目錄2個字符。目錄最多3級,目錄名最多為2個字符。例如上例中"levels=1:2"產生的緩存文件路徑可能是這樣的"/usr/local/nginx/cache_dir/d/f1/50a3269acaa7774c02d4da0968124f1d",註意其中加粗的字體。
  3. keys_zone:定義緩存標識名稱和內存中緩存的最大空間。name部分必須唯一,在後面會引用name來表示使用該緩存方法。
  4. max_size:定義磁盤中緩存目錄的最大空間。即path定義的文件最大空間。

該指令定義後只是定義了一種緩存方法,並非開啟了緩存。

  • proxy_cache:定義要使用哪個緩存方法。使用proxy_cache_path中的name來引用。

例如引用上例定義的cache_one。

proxy_cache cache_cache;
  • proxy_cache_valid:根據狀態碼來指定緩存有效期。

例如,下面的表示狀態碼為200和302的狀態緩存1小時,狀態碼為404時即page not found的緩存只有1分鐘,防止客戶端請求一直錯誤,狀態碼為其他的則緩存5分鐘。

proxy_cache_valid 200 302 1h;
proxy_cache_valid 404 1m;
proxy_cache_valid any 5m;

如果不指定狀態碼,只指定時間,則默認只緩存狀態碼200、301、302各5分鐘,其他的狀態碼不緩存。

以下是代理服務器192.168.100.29上定義的緩存示例。

http {
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;

        upstream web_group {
                server 192.168.100.28 weight=2 max_fails=2 fail_timeout=2s;
                server 192.168.100.30 weight=1 max_fails=2 fail_timeout=2s;
                server 127.0.0.1:80 backup;
        }
        proxy_cache_path /usr/local/nginx/cache_dir levels=1:2 keys_zone=cache_one:20m max_size=1g;
        server {
                listen 127.0.0.1:80;
                root /www/longshuai/;
                index index.html;
        }
        server {
                listen       80;
                server_name  www.longshuai.com;
              # 在響應報文中添加緩存首部字段
                add_header X-Cache "$upstream_cache_status from $server_addr";
                location / {
                        proxy_pass http://web_group;
                        proxy_cache cache_one;
                        proxy_cache_valid 200 1h;
                        proxy_cache_valid 404 1m;
                        proxy_cache_valid any 5m;
                }

        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

其中添加的一行"add_header X-Cache "$upstream_cache_status from $server_addr";"表示在響應報文的頭部加上一字段X-Cache,其值為是否命中緩存的狀態($upstream_cache_status),從哪臺服務器上($server_addr)取得的緩存。 重載代理服務器的配置文件後,在客戶端打開"開發者工具"進行測試。

技術分享

由於是第一次提供緩存功能,所以結果是未命中緩存。此時已經將緩存保存下來了。 再進行測試,結果將命中緩存是"hit from 192.168.100.29"。

技術分享

查看緩存目錄。

[root@xuexi nginx]# tree /usr/local/nginx/cache_dir/  
/usr/local/nginx/cache_dir/
├── 3
│   └── 26
│       └── 3abcc5796b407cf3db2716539d256263
└── d
    └── f1
        └── 3b37290aabefe7369a4680875f763f1d

如果想要刪除緩存,只需刪除對應的目錄即可。

回到Linux系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7048359.html

回到網站架構系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7576137.html

回到數據庫系列文章大綱:http://www.cnblogs.com/f-ck-need-u/p/7586194.html

轉載請註明出處:http://www.cnblogs.com/f-ck-need-u/p/7684732.html

註:若您覺得這篇文章還不錯請點擊右下角推薦,您的支持能激發作者更大的寫作熱情,非常感謝!

nginx的反向代理功能和緩存功能