循序漸進nginx(二):反向代理、負載均衡、快取服務、靜態資源訪問

隨風行雲發表於2020-07-22


前置知識章節:
1.介紹、安裝、hello world、location匹配
2.▶▶反向代理、負載均衡、快取服務、靜態資源訪問✅
3.日誌管理、http限流、https配置,http_rewrite模組,第三方模組安裝,結語。✅


反向代理


?代理有正向代理和反向代理
?正向代理:
所謂的正向,就是以請求發起為角度的,此時代理的是使用者發起的請求。
使用者A無法訪問G網,但A能訪問B網,而B網能訪問G網。那麼如果A先通過訪問B網,B網幫他訪問G網,返回資料給他,那麼此時B網就作為了一個代理伺服器,而且是正向代理。此時A其實是知道它要訪問G網的,G網並不知道是A訪問它的,所以此時是代理了A,正向代理。

?反向代理:
相對於正向代理的代理使用者發起的請求,反向代理代理的是資源伺服器的請求。
使用者A訪問B網的某個資源,但B網沒有,然後它發給C網,C網返回給B網,B網返回給C網。此時使用者A不知道它訪問的是C網,所以此時代理的是C網,反向代理。
場景一般是,A能訪問B,但不能訪問C,B能訪問C。我們使用B這臺機來對外提供服務,讓關於C的請求都先經過B,B再請求C來返回結果。
反向代理有時候用在內網中,用來做請求轉發到內網,這樣一定程度的保護了內網的資源和利用上了內網的伺服器。當然對於我們業務來說,伺服器有可能並不是部署在內網中的,也可以用在外網伺服器代理上。

?反向代理是一個非常重要的功能,可以說nginx你最常用的功能或許就是反向代理了,動靜分離、負載均衡和快取有時候你可能用不上。


使用

1.建立代理目標服務端:

我們首先需要建立一個web服務端,由於我是主攻java的,所以我這裡部署一個java的web伺服器,並讓他監聽8081埠。(192.168.48.131是我的虛擬機器的IP,nginx和這個java web服務端都部署在這裡。)
20200614012418
訪問一下這個要被代理的服務端的介面http://192.168.48.131:8081/user/list,發現呼叫成功。
20200614001834


2.配置nginx反向代理目標服務端:

?2.1 修改server:
因為此時是作為一個代理伺服器,所以我們要新建一個server塊來充當代理伺服器。


❓有人有點疑惑,上面的例子只配了location,我們為什麼要配server呢?
其實這裡是從業務需求來做區分的。因為我們現在的目標是弄一個代理伺服器,當然了你也可以不建立,然後把下面的location放到之前的server下即可。什麼時候是必須建立的呢?當server_name不一樣的時候,但這個也是需要你根據業務來判斷的,比如你原本使用80埠接收發過來的請求,而現在使用8080埠接收發過來的請求的時候就需要一個新的server_name。
這裡新建一個server其實也有介紹server_name的用法的意思。


修改server_name:server_name是當前服務端監聽的地址的意思,
server_name支援幾種語法:

  • 基於確切的域名,域名可以一個或多個,多個使用空格隔開server_name example.com www.example.com
  • 支援萬用字元的方式,萬用字元*只能使用在開頭或者結尾,不能使用在中間。當有多個匹配結果的時候,會選擇最長的匹配結果server_name *.example.com www.example.*
  • 基於正規表示式,當使用正規表示式的時候,開頭必須加上一個~server_name ~^www\d+\.example\.com$;
  • 正規表示式支援<>來獲取一個變數到後面使用以實現二級域名的功能,這裡不講,有興趣自查。
  • 上面幾個語法的優先順序是:
    • 精確域名 > 萬用字元在前 > 萬用字元在後 > 正規表示式

?2.2 修改location
?proxy_pass用於設定被代理伺服器的地址,可以是主機名稱(https://www.baidu.com這樣的)、IP地址(域名加埠號)的形式。
?下面的這個location的意思是,如果請求路徑開頭是/api的,那麼都代理到proxy_pass指定的地址,比如訪問了/api/user/list,那麼得到的結果是http://localhost:8081/user/list的結果。

server {
  listen 8080;

  location /api/ {
    proxy_pass http://localhost:8081/;
  }
}

?在lcoation都是location /api/時,proxy_pass不同,請求的資源也是不一樣的:

  • proxy_pass http://localhost:8081;:請求nginx主機IP:8080/api/user/list,nginx會將該請求代理轉發到http://locahost:8081/api/user/list
  • proxy_pass http://localhost:8081/;:請求nginx主機IP:8080/api/user/list,nginx會將該請求代理轉發到http://locahost:8081/user/list
  • proxy_pass http://localhost:8081/test;請求nginx主機IP:8080/api/user/list,nginx會將該請求代理轉發到http://locahost:8081/testuser/list
  • proxy_pass http://localhost:8081/test/;請求nginx主機IP:8080/api/user/list,nginx會將該請求代理轉發到http://locahost:8081/test/user/list


3.測試使用:

在前面,我建立了一個8081的web服務端,而且http://nginx伺服器IP:8081/user/list是有一個介面的.
20200614001834
我們試一下使用8080來代理一下這個8081這個服務端:
注意下面的這個server如果寫在default.conf的時候,是與其它server塊同級的
20200614002940

如果我們能夠通過8080來訪問到http://192.168.48.131:8081/user/list這個介面,那麼就說明了我們的反向代理成功了。此時發向8080埠的,以/api開頭的請求都會代理到8081中。

20200614003048


## 其他反向代理指令 下面的其他反向代理指令我解釋了一下用途,有需要就自己去了解一下吧。 * proxy_set_header:在將客戶端請求傳送給後端伺服器之前,更改來自客戶端的請求頭資訊。 * proxy_connect_timout:配置nginx與後端伺服器嘗試建立連線的超時時間。 * proxy_read_timeout:定義用於從代理伺服器讀取響應的超時。 * proxy_send_timeout:設定用於將請求傳輸到代理伺服器的超時。 * proxy_redirect:用於修改後端伺服器返回的響應頭中的Location和Refresh。[proxy_redirect](http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_redirect)

負載均衡

負載均衡其實也算是基於反向代理的。

上面的反向代理提到一點反向代理可以為我們的服務端進行代理,你有時候可能是多個服務端提供同一功能的,為了讓他們能夠平攤壓力,那麼你可能需要負載均衡功能。


使用

1.準備服務端

?準備負載均衡用的業務服務端,我這裡給的是一個java Spring Boot端的簡單程式碼,我部署成多個服務端的時候,由於埠不同,訪問info介面返回的資料也不同,這樣就可以測試是否達到了負載均衡。
20200615152747

?部署服務端,使用--server.port來部署到不同的埠,例如java -jar a-simple-web-0.0.2-SNAPSHOT.jar --server.port=8082就把我的jar程式部署到了8082埠。



2.修改nginx配置

我們這裡新建一個conf.d/loadbalance.conf,由於nginx.conf內部有一個include /etc/nginx/conf.d/*.conf;可以把conf.d下的配置檔案都匯入到nginx.conf的http塊中,所以我們新建的這個conf也是可以匯入到nginx.conf中的。
20200615155759



3.測試

訪問http://192.168.31.128:9001/user/info,看是否是輪詢的分發請求,如果響應的時候返回了不同的埠,那麼就證明了是輪詢的分發請求。



負載均衡策略

?預設輪詢負載均衡:在不指定負載均衡策略的時候,預設的策略是按順序給負載均衡服務端傳送請求,並且一次順序中每個服務端處理兩次請求,比如兩個服務端,那麼就是AABBAABB這樣迴圈下去。 如果服務端有當機的,會從負載均衡順序中去除。等到當機的服務端可用後,會在30S(好像是)之後加入到可用負載均衡服務端列表中。


?加權輪詢負載均衡:在輪詢的基礎上加上權重的考慮,假如服務端A的權重是1,服務端B的權重是2,那麼6個請求中,服務端A會收到2個請求,服務端B會收到4個請求。
20200615155954


?ip_hash負載均衡:每個請求按ip的雜湊結果分配到指定的負載服務端響應,(原理類似求餘數,把服務端排序之後,根據雜湊處理再處理之後得到的餘數來選取服務端),這樣同一個ip響應的服務端是固定的。可以在一定程度解決服務端Session共享的問題。但這樣可能負載壓力就分配的不平均了。
語法例子:

upstream ip-hash {
  ip_hash;
  server localhost:8081;
  server localhost:8082;
}

?也可以使用第三方模組來負載均衡。但由於我們上面沒有講過第三方模組,引入前置知識需要佔用大量篇幅,所以這裡只引出一下,有需要的自查吧。

  • 第三方模組fair:可以基於響應時間分配請求,優先分配到響應時間短的服務端上。
  • 第三方模組url_hash:可以基於url的hash結果來分配請求。


負載均衡的額外引數

上面介紹了使用weight引數來實現加權輪詢負載均衡,其實還有一些其他的引數。

  • max_fails:允許請求失敗的次數。預設值是1。
  • fail_timeout:重新檢測服務的時間,當服務端請求失敗後,會在fail_timeout時間內標記成不可用,fail_timeout時間之後再次檢測是否可用,不行就再等fail_timeout時間之後再次檢測是否可用。預設是10S.
  • backup:預留的備份服務端。只有當其他服務端都當機或者處於忙碌狀態時,才會分發請求給backuo標註的服務端。
  • down:暫時不參與負載均衡的服務端,只有其他服務端都當機的時候,這個服務端才參與負載均衡。
  • max_conns:限制接收的最大的連線數。
upstream web-server {
  server localhost:8081 max_fails=1 fail_timeout=10;
  server localhost:8082;
  server localhost:8083 backup;
  server localhost:8084 down;
}

?使用ip_hash時,不能使用weight和backup。




快取服務


?在瀏覽器訪問某個網站的資源的時候,會看瀏覽器是否快取了這個資料。如果本地有這個快取資料,那麼就會直接從本地中獲取了,不會再請求網路了。這是客戶端快取。nginx傳遞的響應的某些資料會影響瀏覽器是否快取資料以及快取多久。
?除了客戶端快取,還有一種代理快取nginx的快取是代理快取,因為它其實是將代理的服務端的結果快取了。代理快取使用ngx_http_proxy_module的指令來配置。
?(除了這兩種,還有(後端)服務端快取,也就是使用redis等技術進行的後端服務端快取。)。


代理快取

語法介紹

?proxy_cache_path path:用來定義快取檔案路徑。只能用在http塊。
語法:

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];
  • path是快取檔案存放路徑
  • levels用於定義檔案存放層級,可以有一層(1),兩層(1:2),三層(1:2:2)。目錄會根據請求URL地址的雜湊結果來建立(從末尾開始擷取,),假如雜湊結果是9cad383e7b0ee3d1d4b7099aace20b3f,那麼levels=1:2代表第一層目錄為長度為一的字元f,第二層是長度為2的字元b3
  • keys_zone用來定義當前這個快取存放空間的名字,10m是一個大小
  • max_size用來定義目錄最大的大小。
  • inactive用來定義不活躍的快取多久清除。
  • use_temp_path:快取臨時目錄。一般可以不定義。


?proxy_cache zone|off:可以用在http, server, location。zone是proxy_cache_path的keys_zone.
?proxy_cache_valid [code...] time:快取過期週期,過期之後就不返回代理快取了。 code是http狀態碼。
?proxy_cache_key string:配置快取的標識,比如說請求不一樣的話那肯定不返回同樣的代理快取了,這就是是否返回同一個快取的區分標識。預設值是proxy_cache_key $scheme$proxy_host$request_uri;,可以用在http, server, location。其他例子:proxy_cache_key $scheme$proxy_host$uri$args;



使用例子

下面來做一個實驗:嘗試使用nginx的代理快取作為代理的響應。


1.我們新建一個後端介面。這個後端介面能在每一次訪問的時候都在控制檯列印出一個訊息(下面是一個Spring Boot例子):
20200622222735
2.配置反向代理:
20200622223500

3.一開始先測試一下http://192.168.31.128/api/user/info,看每一次訪問是否都向後端發起了請求,確實是每一次都發請求的:
20200622223617

4.然後配置代理快取:
20200626003102

5.再次測試訪問http://192.168.31.128/api/user/info是否每一次訪問都向後端發起了請求,結果應該是後端服務端只會打出一次,或者你可以嘗試關閉後端服務端,然後訪問,如果還是能夠訪問,那麼應該是已經代理快取成功了。



代理快取補充:

?永久快取:上面使用proxy_cache配置的其實是臨時快取。也就是說一定時間後會自動過期。如果你的資料在很長很長時間都不會過期,那麼可以考慮使用proxy_store.
?如果你想讓部分請求不要快取,可以使用proxy_no_cache [string...],string可以是變數,如果存在某個string值不為空也不為0,那麼此請求不會被快取。
?快取清理:incative配置會幫我們清除過期的快取檔案,但還沒過期的不會清除,需要我們手動清除(場景是比如說你更新了大量資料,此時快取中的資料很多都錯誤了,此時需要清除所有快取。),如果你需要清除的話,那麼一種方法是手動定義一個Linux指令碼來清除快取;一種方法是使用模組nginx-cache-purge。這些由於篇幅問題,不講述。



瀏覽器快取

瀏覽器是怎麼判斷快取是否需要使用本地快取以及快取是否過期的呢?它通過響應頭中的expirecache-controlLast-ModifiedEtag等頭資訊來判斷的。


  • 用於本地校驗是否過期的頭:expireCache-control(max-age),如果有本地快取,那麼會使用expire來判斷是否過期,不過期,直接使用本地快取,本地過期之後,再進行遠端校驗。
  • 用於遠端校驗的Last-Modified頭資訊:用於遠端檔案修改校驗的,是一個GMT時間,例如Thu, 02 Jul 2020 01:05:03 GMT,如果校驗時間不一致,那麼不使用本地快取,一致則使用本地快取。
  • 用於遠端校驗的的Etag頭資訊:用於遠端檔案修改校驗的,是一個類時間戳的資料,例如:"5efd32bf-3fa8e",如果校驗時間不一致,那麼不使用本地快取,一致則使用本地快取。Etag與Last-Modified的區別是,Etag更精確,所以會優先判斷Etag,然後再判斷Last-Modified。
  • 相關nginx模組:ngx_http_headers_module

❗瀏覽器快取機制你可以自己瞭解一下:部落格園-HTTP快取機制


?Expire

  • 語法:expires [modified] time;
    • modified用於執行修改後過期,比如expires modified +24h;就代表修改後24小時內不過期。
    • time:過期時間,例如有expires 24h;24天不過期,expires 30d;30天不過期,expires -1;代表永不過期

測試

測試之前我們先要提幾點:

  • 瀏覽器有預設的快取策略,Etag和Last-Modified預設是自帶的,會有基於對檔案修改的快取,第一次響應200之後,第二次響應為304的時候,瀏覽器還是會發請求,但此時發請求用於測試檔案是否過期,不過期則不會傳輸檔案
  • 當配置了expires的時候怎麼判斷它生效了呢?響應碼為200,並且不對nginx發起請求。
  • expires並不會在任何情況都生效,比如說F5重新整理Ctrl+F5重新整理就無效,此時測試應該使用在連結欄按回車發請求來測試。expires可用於什麼情況可以參考部落格園-HTTP快取機制

1.我們先在/usr/share/nginx/html下面儲存一張圖片,後面會通過訪問這種圖片,來測試客戶端快取。


2.我們一開始先不要配置expires:

server {
    listen       80;
    server_name  127.0.0.1;
    location / {
      root /usr/share/nginx/html;
    }
}

3.多次訪問一下http://192.168.31.128/a.jpg看看nginx的訪問日誌/var/log/nginx/access.log是否有新增
此時要多留意每次請求的響應碼,應當有以下幾個情況:

  • 如果你使用F5重新整理,那麼第一次響應碼為200,後面都是304,第一個響應碼為200的時候用於獲取檔案,後面的304會檢查檔案是否修改,不修改則使用快取的檔案,此時每次重新整理都應該發了一次請求,access.log中可以觀察到。
  • 如果你使用在連結欄按回車發請求來測試,那麼響應碼應當都是200,是每一次都會去請求檔案,此時每次按回車都應該發了一次請求,access.log中可以觀察到。

4.加了expires之後看看,我們簡單的使用兩分鐘看看。

server {
    listen       80;
    server_name  127.0.0.1;
    location / {
      expires 2m;
      root /usr/share/nginx/html;
    }
}

5.多次訪問一下http://192.168.31.128/a.jpg看看nginx的訪問日誌是否有新增

  • 如果你使用F5重新整理,由於expires不會在F5重新整理時生效,所以效果應該和未配置之前是一樣的。
  • 如果你使用在連結欄按回車發請求來測試,那麼一開始響應碼應當都是200但不會真正的發起請求,而是使用快取中的資料,access.log中不會看到請求。(因為我上面定義快取是兩分鐘)兩分鐘之後第一次請求是304,用於校驗檔案是否修改,如果沒修改,那麼後面的兩分鐘之內的響應又是不發請求的200。


靜態資源訪問

?nginx可以對外接收靜態資源訪問的請求。

?在上面的location的內容的時候,其實有講到返回檔案資源的知識。
比如location ~* \.(gif|jpg|jpeg)$匹配任何以.gif、.jpg 或 .jpeg 結尾的請求,然後你發的請求匹配成功的時候,會使用root+location得到的路徑的資源作為響應。其實這些也就是靜態資源了,所以其實也可以通過nginx來達到靜態資源的訪問。

?在前後端分離之後,前端作為靜態資源訪問,應該部署到哪裡呢?因為我們此時是不應該把前端靜態資源部署到後端伺服器上的。那麼這時候放到nginx服務端上也是可以的。利用nginx的靜態資源訪問作為前端的服務端。

?或者你也可以把nginx作為檔案資源訪問的服務端。




相關文章