Nginx快取原理及機制

逆月翎發表於2019-09-23

文章原創於公眾號:程式猿周先森。本平臺不定時更新,喜歡我的文章,歡迎關注我的微信公眾號。file

上篇文章介紹了Nginx一個較為重要的知識點:Nginx實現介面限流。本篇文章將介紹Nginx另一個重要知識點:Nginx快取原理。其實說到快取技術大家應該都不會很陌生,快取技術的基本思想其實是對使用者已經訪問過的內容在Nginx建立副本,如果在一段時間內(快取尚未過期)再次訪問該資料,則不需要重新發起請求獲取資料,可以直接從快取中讀取到該資料,好處在於減少了Nginx與後端服務之間的網路互動,減輕了網路的壓力,而且在減少資料傳輸的延遲時同時可以提升使用者訪問速度。而且如果碰上後端服務出現異常時,還可以通過快取進行相應使用者請求,提高了後端服務的穩定性。

什麼是Nginx快取?

Nginx基於Proxy Store實現,使用Nginx的http_proxy模組可以實現類似於squid的快取功能。當啟用快取時,Nginx會將相應資料儲存在磁碟快取中,只要快取資料尚未過期,就會使用快取資料來響應客戶端的請求。

如何啟用快取?

Nginx啟用快取需要在最頂層的http節點下配置proxycachepath命令。我們先看看proxycachepath命令的語法結構:

  • proxycachepath /data/cache keys_zone=niyueling:10m;

可以看到proxycachepath命令一共包含兩個引數,第一個引數指定快取儲存的本地路徑,第二個引數定義快取資料的共享記憶體區域的名稱和記憶體區大小。Nginx啟動後,快取載入程式只進行載入一次,載入時會將快取的後設資料載入到共享記憶體區域,但是如果一次載入整個快取全部內容可能會使Nginx剛啟動的前幾分鐘效能消耗嚴重,大幅度降低Nginx的效能。所以可以在proxycachepath命令中配置快取迭代載入。快取迭代載入一共可以設定三個引數:

  • loader_threshold - 迭代的持續時間,以毫秒為單位(預設為200)
  • loader_files - 在一次迭代期間載入的最大專案數(預設為100)
  • loader_sleeps - 迭代之間的延遲(以毫秒為單位)(預設為50)

我們可以看下一個小例子:

  • proxycachepath /data/cache keyszone=niyueling:10m loaderthreshold=300 loader_files=200;

在這個例子中快取迭代載入可以持續300毫秒或者直到載入滿200個專案。在http節點下設定完proxycachepath命令,下一步在虛擬伺服器配置中配置proxycache命令,我們可以看看proxycache命令的語法結構:

  • proxy_cache niyueling;

可以看到proxycache命令很簡單,就是指定了我們剛才配置的記憶體區。但是這裡有一點需要額外注意的是:我們剛才通過配置proxycachepath命令的keyszone引數配置記憶體區大小為10m,這並不會限制快取資料的大小,實際上快取資料是儲存在檔案系統中的特定檔案的後設資料副本。如果想要限制快取資料的上限,則需要在proxycachepath命令中新增maxsize引數設定快取資料上限。說完了proxycache命令。我們接著看看下一個命令:proxycachemethods,我們看下該命令語法結構:

  • proxycachemethods[GET HEAD POST];

在虛擬伺服器下配置proxycachemethods命令可以指定該虛擬伺服器下什麼型別的HTTP方法可以被快取。預設情況下GET請求及HEAD請求會被快取,而POST請求不會被快取。接下來看看另外一個常見的命令:proxycachevalid,先貼下該命令語法結構:

  • proxycachevalid replycode [replycode...] time;

這個命令很有意思,在虛擬伺服器下設定該命令,它可以針對不同狀態碼的響應資料設定不同的快取時間,我們可以看個簡單的小例子:

  • proxycachevalid 200 10m ;
  • proxycachevalid 404 1m ;
  • proxycachevalid 302 5m ;

我們通過上面的命令就可以設定200狀態碼的快取時間為10分鐘,302重定向的快取時間為5分鐘,404的快取時間為1分鐘。如果想為所有狀態碼定義相同快取時間,就可以使用any作為第一個引數:

  • proxycachevalid any 5m;

接下來看看下一個命令:proxycachebypass。一樣先看下語法結構:

  • proxycachebypass $cookienocache $argnocache$arg_comment;

這個命令可以配置不會向客戶端響應快取,而是直接將請求轉發給後端服務進行請求資料。可以通過上述命令配置需要繞過快取的請求URL,也就是說URL中包含該配置的值,則這次請求會直接跳過快取直接請求後端服務去獲取資料。接下來還有最後一個比較常用的命令:proxycachemin_uses。先貼下語法結構:

  • proxycachemin_uses 2;

這個命令可以設定當某請求最少響應幾次後會被快取。若我設定為2則表示每個請求最少被請求2次後會加入到快取中。

Nginx清除快取如果快取過期則需要從快取中刪除過期的快取檔案,防止新舊快取出現交錯出錯,當Nginx接收到自定義HTTP頭或者PURGE請求時,快取將會被清除。

配置快取清除我們在HTTP節點下建立一個新變數$purge_method來標識使用PURGE方法的請求並刪除匹配的URL。

http {

map $requestmethod $purgemethod {

PURGE 1;

default 0;

}

}

進入虛擬伺服器配置,在location中配置快取記憶體,並且指定快取清除請求命令proxycachepurge。

server {

listen 80;

server_name www.niyueling.cn;

location / {

proxy_cache niyueling;

proxycachepurge $purge_method;

}

}

傳送清除命令

配置proxycachepurge指令後需要傳送PURGE請求來清除快取。例如我們使用PURGE方式請求url:

  • PURGE www.niyueling.cn/getArticle

則getArticle對應的快取中的資料將被刪除。但是,這些快取記憶體資料不會從快取中完全刪除,它們將保留在磁碟上,直到它們被刪除為非活動狀態,或由快取清除程式處理。

限制IP訪問清除命令

清除快取這種命令一般需要許可權才可進行操作,所以我們一般需要配置允許傳送快取清除請求的IP地址:

geo $purge_allowed {

default 0;

49.235.28.88 1;

192.168.1.100/24 1;

}

map $requestmethod $purgemethod {

PURGE $purge_allowed;

default 0;

}
當Nginx接收到清除快取請求時,Nginx檢查客戶端IP地址,若IP地址已經獲得清除快取許可權,則$purgemethod設定為$purgeallowed,值為1表示允許清除快取,值為0表示表示IP地址未獲得許可權。

從快取中完全刪除檔案

剛才說過了快取記憶體資料不會從快取中完全刪除,它們將保留在磁碟上,直到它們被刪除為非活動狀態,或由快取清除程式處理。要完全刪除與getArticle相匹配的快取資料,需要在proxycachepath新增引數purger,該參數列示永久的遍歷所有快取條目,並刪除與萬用字元相匹配的條目。

  • proxycachepath /data/cache keys_zone=niyueling:10m purger=on;

位元組快取當我們請求一個大檔案時,因為請求比較耗時,當有下一個請求來臨時將不得不等待整個大檔案被下載並放入快取記憶體。Nginx用快取片模組填充快取記憶體。可以將大檔案分為較小的切片,每個範圍請求選擇將覆蓋所請求範圍的特定切片,並且如果此範圍切片仍未快取,就將其放入快取中。啟用位元組範圍快取需要注意兩個條件是否滿足:

  • 確保Nginx是使用模組編譯的。
  • 使用slice指令指定切片的大小。

可以使用slice命令指定切片大小:

location / {

slice 1m;

}

使用slice指令指定切片大小時應注意切片大小應適當調整,使切片快速下載。因為切片大小指定太小可能會導致記憶體使用量過多和大量開啟的檔案描述符,切片大小指定太大的值可能會導致請求延遲。

接著將$slice_range變數加入到快取鍵中:

  • proxycachekey $uri$isargs$args$slicerange;

使用206狀態程式碼快取響應,快取有效期30m:

  • proxycachevalid 206 30m;

然後設定Range頭傳遞$slice_range變數來將傳遞範圍請求:

  • proxysetheader Range $slice_range;

位元組快取小案例:

location / {

slice 1m;

proxy_cache niyueling;

proxycachekey $uri$isargs$args$slicerange;

proxysetheader Range $slice_range;

proxycachevalid 206 30m;

}

快取清除小案例

http {

proxycachepath /data/cache keys_zone=niyueling:10m purger=on;

map $requestmethod $purgemethod {

PURGE 1;

default 0;

}

server {

listen 80;

server_name www.niyueling.cn;

location / {

proxy_cache niyueling;

proxycachepurge $purge_method;

}

}

geo $purge_allowed {

default 0;

49.235.28.88 1;

192.168.1.100/24 1;

}

map $requestmethod $purgemethod {

PURGE $purge_allowed;

default 0;

}

}

如果喜歡我的文章,歡迎關注公眾號:程式猿周先森。file

相關文章