nginx學習筆記之三:nginx作爲反向代理服務器

  nginx不僅能作爲web server,還具有反向代理、負載均衡和緩存的功能

wKioL1c8kEfCGsYKAABvCQ5CZbM251.png


一、nginx的反向代理和負載均衡功能


  1、proxy模塊

     nginx通過proxy模塊實現將客戶端的請求代理至上游服務器,此時nginx與上游服務器的連接是通過http協議進行的。nginx在實現反向代理功能時的最重要指令爲 proxy_pass,它能夠並能夠根據URI、客戶端參數或其它的處理邏輯將用戶請求調度至上游服務器上(upstream server)。

    ⑴proxy_pass URL;

       設置後端服務器的協議和地址;這條指令可以設置的協議是“http”或者“https”,而地址既可以使用域名或者IP地址加端口(可選)的形式來定義:

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

       如果解析一個域名得到多個地址,所有的地址都會以輪轉的方式被使用。當然,也可以使用服務器組來定義多個地址。

       如果proxy_pass沒有使用URI,傳送到後端服務器的請求URI一般是客戶端發起的原始URI,如果nginx改變了請求URI,則請求路徑與配置中的路徑的匹配部分將被替換爲指令中定義的URI:

         若nginx接到的請求的uri是/name/a.html

           location /name/ {
              proxy_pass http://192.168.30.20/remote/;
           } #傳送到後端服務器的URI是/remote/a.html

           location /name/ {
              proxy_pass http://192.168.30.20;
           } #傳送到後端服務器的URI是/name/a.html

           location /name/ {

              proxy_pass http://192.168.30.20/;

           } #注意與上面用法的區別,這裏地址末尾帶有斜線,實際上被認爲定義了URI,該“/”會替換“/name/",傳送到後端服務器的URI是/a.html。

        如果使用正則表達式定義路徑,則proxy_pass指令不應使用URI。例如:

           location ~ ^/bbs {

             proxy_pass http://www.magedu.com;

           }

        在需要代理的路徑中,使用rewrite指令改變了URI,那麼nginx將使用重寫後的URI處理請求,而忽略proxy_pass指令設置的URI。如下面所示的例子中,傳送給上游服務器的URI爲/index.php?page=<match>。

           location / {

             rewrite /(.*)$ /index.php?page=$1 break;

             proxy_pass http://localhost:8080;

           }


    ⑵proxy模塊的其它指令

     ①proxy_connect_timeout time;

         與後端服務器建立連接的超時時間。一般不可能大於75秒;

     ②proxy_cookie_domain off;  #取消當前配置級別的所有proxy_cookie_domain指令

      proxy_cookie_domain domain replacement;

         設置“Set-Cookie”響應頭中的domain屬性的替換文本,其值可以爲一個字符串、正則表達式的模式或一個引用的變量;例如:

           proxy_cookie_domain localhost example.org;

         說明:

           瀏覽器對 Cookie 有很多限制,如果 Cookie 的 Domain 部分與當前頁面的 Domain 不匹配就無法寫入。所以如果請求 A 域名,服務器 proxy_pass 到 B 域名,然後 B 服務器輸出 Domian=B 的 Cookie,前端的頁面依然停留在 A 域名上,於是瀏覽器就無法將 Cookie 寫入。
           不僅是域名,瀏覽器對 Path 也有限制。我們經常會 proxy_pass 到目標服務器的某個 Path 下,不把這個 Path 暴露給瀏覽器。這時候如果目標服務器的 Cookie 寫死了 Path 也會出現 Cookie 無法寫入的問題。

     ③proxy_cookie_path off;

      proxy_cookie_path path replacement;

         設置“Set-Cookie”響應頭中的path屬性的替換文本,其值可以爲一個字符串、正則表達式的模式或一個引用的變量;例如:

           proxy_cookie_path /two/ /;           

           若“Set-Cookie”響應頭含有屬性“path=/two/some/uri/”,那麼該指令會將這個屬性改寫爲“path=/some/uri/”。

      ④proxy_hide_header field

         nginx默認不會將“Date”、“Server”、“X-Pad”,和“X-Accel-...”響應頭髮送給客戶端。該指令則可以設置額外隱藏的響應頭,這些響應頭也不會發送給客戶端。相反的,如果希望允許傳遞某些響應頭給客戶端,可以使用proxy_pass_header指令。

      ⑤proxy_set_header field value;

         重新定義或者添加發往後端服務器的請求頭。value可以包含文本、變量或者它們的組合。例如:

           proxy_set_header X-Real-IP $remote_addr;   #給請求頭中添加客戶端IP

           proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;   

         默認情況下,只有兩個請求頭會被重新定義:

           proxy_set_header Host $proxy_host;
           proxy_set_header Connection close;

         如果某個請求頭的值爲空,那麼這個請求頭將不會傳送給後端服務器:

           proxy_set_header Accept-Encoding "";

      ⑥proxy_pass_request_headers on|off;   #是否將http首部發往上游服務器

      ⑦proxy_pass_request_body on|off;  #是否將http請求報文的包體部分發往上游服務器

      ⑧proxy_redirect [default|off|redirect replacement];

         修改上游服務器傳來的響應頭中的"Location"和"Refresh"字段。例如:

           proxy_redirect http://localhost:8000/two/ http://frontend/one/;       

         replacement字符串可以省略服務器名:

           proxy_redirect http://localhost:8000/two/ /;

           此時將使用代理服務器的主域名和端口號來替換。如果端口是80,可以不加。

      ⑨proxy_send_timeout time;

         在連接斷開之前兩次發送至upstream server的寫操作的最大間隔時長;

      ⑩proxy_read_timeout time;

         在連接斷開之前兩次從接收upstream server接收讀操作的最大間隔時長;


    ⑶proxy模塊的內置變量

      ①$proxy_host:後端服務器的主機名和端口;

      ②$proxy_port:後端服務器的端口;

      ③$proxy_add_x_forwarded_for

         將$remote_addr變量值添加在客戶端“X-Forwarded-For”請求頭的後面,並以逗號分隔。 如果客戶端請求未攜帶“X-Forwarded-For”請求頭,$proxy_add_x_forwarded_for變量值將與$remote_addr變量相同        


  2、upstream模塊

      如果有多個上游服務器,我們可以把它們放到一個組中,並且給它們賦予不同的權重和類型,進行負載均衡等,這些功能是由upstream模塊實現的。

    ⑴配置語法:

       upstream name {

          server address [parameters];

          ...

       }

     示例:

        upstream backend {
           server backend1.example.com       weight=5;
           server backend2.example.com:8080;
           server unix:/tmp/backend3;
           server backup1.example.com:8080   backup;
         }

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

    ⑵uptream使用注意:

       ①只能用於http上下文

       ②各server只能直接使用IP或主機名,不要加協議

    ⑶默認情況下,nginx按加權輪轉的方式將請求代理到各上游服務器。與上游服務器通信的時候,如果出現錯誤,請求會被傳給下一個服務器,直到所有可用的服務器都被嘗試過。如果所有服務器都返回失敗,客戶端將會得到最後通信的那個服務器的(失敗)響應結果。

    ⑷地址可以是域名或者IP地址,端口是可選的(默認是80),或者是指定“unix:”前綴的UNIX域套接字的路徑。如果一個域名解析到多個IP,本質上是定義了多個server。

    ⑸server後可定義的參數:

       ①weight=number   #設定服務器的權重,默認是1。

       ②max_fails=number

           設定Nginx與服務器通信的嘗試失敗的次數。在fail_timeout參數定義的時間段內,如果失敗的次數達到此值,Nginx就認爲服務器不可用。在下一個fail_timeout時間段,服務器不會再被嘗試。失敗的嘗試次數默認是1。設爲0就會停止統計嘗試次數,認爲服務器是一直可用的。

       ③fail_timeout=time   #默認是10秒

          設定:

             統計失敗嘗試次數的時間段。在這段時間中,服務器失敗次數達到指定的嘗試次數,服務器就被認爲不可用。

             服務器被認爲不可用的時間段。        

       ④backup   #標記爲備用服務器。當主服務器不可用以後,請求會被傳給這些服務器。

       ⑤down   #標記服務器永久不可用,可以跟ip_hash指令一起使用

    ⑹upstream模塊的其它負載均衡算法(用於upstream上下文):

       ①ip_hash;

          作用同lvs中的sh調度算法,將來自於同一個客戶端的請求始終調度至同一臺後端服務器(除了當服務器不可用的時候)

       ②least_conn;

          將請求發送到活動連接數最少的那臺服務器。如果這樣的服務器有多臺,就嘗試按加權輪循來調度

       ③sticky cookie name [expires=time] [domain=domain] [httponly] [secure] [path=path];

          session綁定,將來自於同一個客戶端的請求始終調度至同一臺後端服務器,從而實現客戶端與後端服務器的session保持。

          ip_hash指令無法實現對內網NAT用戶的均衡,而sticky指令可以做到;

          ◆sticky工作原理:             

             1.瀏覽器首次發起請求,請求頭未帶cookie。nginx接收請求,發現請求頭沒有cookie,則以輪詢方式將請求代理給後端服務器。

             2.後端服務器處理完請求,將響應頭和內容返回給nginx。

             3.nginx生成cookie,返回給客戶端瀏覽器。cookie的值與後端服務器對應,可能是明文,也可能是md5、sha1等Hash值。

             4.瀏覽器接收請求,並創建cookie。

             5.瀏覽器再次發送請求時,帶上cookie。

             6.nginx接收到cookie,直接轉給對應的後端服務器

          參數說明:             

             domain:cookie作用的域名

             path:cookie作用的路徑

             expires:cookie的過期時長

          示例: 

                    upstream backend {
                       server backend1.example.com;
                       server backend2.example.com;

                       sticky cookie srv_id expires=1h domain=.example.com path=/;
                    }

            

    ⑺health_check [interval=time] [fails=number] [passes=number] [uri=uri] [match=name];

        對上游服務器組進行健康狀態檢測,用於location中;

      參數說明:

        interval=time  #檢測的間隔時長,默認爲5秒

        fails=number   #連續檢測失敗多少次即認爲上游服務器不可用,默認爲1次

        passes=number  #上游服務器從不可用到可用狀態時需要連續檢測的次數,默認爲1次

        uri=uri      #定義用於健康檢測的URI,默認爲“/”,即默認檢測目標服務器的主頁

        match=name    #指定一段配置來當作檢測條件,默認當響應碼爲2XX或3XX時認爲上游服務器是可用的

      示例:          

         http {

           server {

             ...

             location / {

               proxy_pass http://backend;

               health_check uri=/.health.html match=welcome;

             }

           }

           match welcome {   #match配置段要位於http上下文

             status 200;

             header Content-Type = text/html;

             body ~ "Welcome to nginx!";

           }

         }

               

  3、fastcgi模塊

     nginx能夠通過fastcgi模塊實現將客戶端的動態文件請求代理至fastcgi server,此時nginx與fastcgi server的通信是通過fastcgi協議進行的        

    ⑴fastcgi模塊的常用指令:

     ①fastcgi_pass address;  #指定fastcgi server的地址和端口,也支持使用unix sock;

         例如:

            fastcgi_pass localhost:9000;

            fastcgi_pass 192.168.30.20:9000;

            fastcgi_pass unix:/tmp/fastcgi.socket;

     ②fastcgi_bind address | off;   #指定聯繫fpm服務器時使用的地址;

     ③fastcgi_param parameter value [if_not_empty];

         定義傳遞給fastcgi server的參數;參數值可以是文本、變量或它們的組合,if_not_empty表示不爲空時才傳遞

         例:fastcgi_param SCRIPT_FILENAME /web/scripts$fastcgi_script_name;

     ④fastcgi_index name;   #默認主頁名;就是當URI中的文件名缺省時,使用此文件名

     ⑤fastcgi_connect_timeout time;   #連接fastcgi服務器的超時時長;

     ⑥fastcgi_send_timeout time;   #向fastcgi服務傳輸數據的超時時長;

    ⑵通常建議nginx和fastcgi server(如php-fpm)部署在同一臺服務器上,因爲二者如果通過網絡通信的話會造成額外的性能開銷。

    ⑶配置示例:

       location ~ \.php$ {

          fastcgi_pass 127.0.0.1:9001;

          fastcgi_index index.php;

          fastcgi_param SCRIPT_FILENAME /web/scripts$fastcgi_script_name;

          include fastcgi_params;

       }

       說明:

         ①參數SCRIPT_FILENAME保存是的腳本文件的絕對路徑;例如,若請求的URI是/test/status.php,那麼向fastcgi server傳遞的腳本文件路徑就是/web/scripts/test/status.php

         ②nginx有兩個文件fastcgi_params和fastcgi.conf,它們存放着nginx向fastcgi server傳遞的參數,二者唯一的區別是後者比前者多了一行 SCRIPT_FILENAME 的定義:

             fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;


    ⑷通過nginx查看後端php-fpm的運行狀態

        vim /etc/php-fpm.d/www.conf(若php-fpm是編譯安裝,則爲主配置文件)

           pm.status_path = fpm_status

           ping.path = fpm_ping

        vim /etc/nginx/nginx.conf           

           location ~* /(fpm_status|fpm_ping) {

             root           /www/a.com;

             fastcgi_pass   127.0.0.1:9000;

             fastcgi_param  SCRIPT_FILENAME  $fastcgi_script_name;

             include      fastcgi_params;

           }        

         

二、nginx的緩存功能

   nginx做爲反向代理時,能夠將來自上游服務器的響應緩存至本地,並在後續的客戶端請求同樣內容時直接從本地構造響應報文。nginx使用磁盤做緩存;

   緩存將遵從上游服務器的響應報文首部中關於緩存的設定,如 "Expires"、"Cache-Control: no-cache"、 "Cache-Control: max-age=XXX"、"private"和"no-store" 等,但nginx在緩存時不會考慮響應報文的"Vary"首部。爲了確保私有信息不被緩存,所有關於用戶的私有信息可以在上游服務器上通過"no-cache" or "max-age=0"來實現,也可在nginx設定proxy_cache_key必須包含用戶特有數據如$cookie_xxx的方式實現,但最後這種方式在公共緩存上使用可能會有風險。因此,在響應報文中含有以下首部或指定標誌的報文將不會被緩存。

      Set-Cookie

      Cache-Control containing "no-cache", "no-store", "private", or a "max-age" with a non-numeric or 0 value

      Expires with a time in the past

      X-Accel-Expires: 0

   與緩存有關的指令:

     ①proxy_cache zone | off;   #定義一個用於緩存的共享內存區域,其可被多個地方調用;

     ②proxy_cache_key string;   #設定在存儲及檢索緩存時用於“鍵”的字符串,可以使用變量爲$uri其值,但使用不當時有可能會爲同一個內容緩存多次;另外,將用戶私有信息用於鍵可以避免將用戶的私有信息返回給其它用戶;

        例如:proxy_cache_key "$host$request_uri;

     ③proxy_cache_lock on | off;   #啓用此項,可在緩存未命令中阻止多個相同的請求同時發往upstream,其生效範圍爲worker級別;

     ④proxy_cache_lock_timeout time;   #proxy_cache_lock功能的鎖定時長;

     ⑤proxy_cache_min_uses number;   #某響應報文被緩存之前至少應該被請求的次數;

     ⑥proxy_cache_path path [levels=levels] keys_zone=name:size [inactive=time] [max_size=size] [loader_files=number] [loader_sleep=time] [loader_threshold=time] ;

        定義一個用記保存緩存響應報文的目錄,及一個保存緩存對象的鍵及響應元數據的共享內存區域(keys_zone=name:size),其可選參數有:

          levels:每級子目錄名稱的長度,有效值爲1或2,每級之間使用冒號分隔,最多爲3級;

          inactive:非活動緩存項從緩存中剔除之前的最大緩存時長;

          max_size:緩存空間大小的上限,當需要緩存的對象超出此空間限定時,緩存管理器將基於LRU算法對其進行清理;

          loader_files:緩存加載器的每次工作過程最多爲多少個文件加載元數據;

          loader_sleep:緩存加載器的每次迭代工作之後的睡眠時長;

          loader_threashold:緩存加載器的最大睡眠時長;

        示例:

          proxy_cache_path /data/nginx/cache/one levels=1 keys_zone=one:10m;

          proxy_cache_path /data/nginx/cache/two levels=2:2 keys_zone=two:100m;          ⑦proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 | http_502 | http_503 |http_504 | http_403 | http_404 | off ...;

        在無法聯繫到upstream服務器時的哪種情形下(如error、timeout或http_500等)讓nginx使用本地緩存的過期的緩存對象直接響應客戶端請求;

     ⑧proxy_cache_valid [code ...] time;

        用於爲不同的響應設定不同時長的有效緩存時長,例如:

          proxy_cache_valid  200 302  10m;

     ⑨proxy_cache_methods GET | HEAD | POST ...;   #爲哪些請求方法啓用緩存功能;

     ⑩proxy_cache_bypass string;

        設定在哪種情形下,nginx將不從緩存中取數據。例如:

          proxy_cache_bypass $cookie_nocache $arg_nocache $arg_comment;

          proxy_cache_bypass $http_pragma $http_authorization;

   nginx也可將後端fastcgi server的響應結果緩存:

      fastcgi_cache

      fastcgi_cache_path

      fastcgi_cache_valid

      ...

      這些指令用法同上述指令類似,不再贅述;

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