Nginx是一個免費開源且穩定高效的Web伺服器程式,擁有反向代理以及負載平衡的功能,經常作為最前端的伺服器。當它用作反向代理伺服器或是PHP網頁伺服器時,無論是proxy_pass還是fastcgi_pass,均有提供快取的功能。然而,免費版本的Nginx並沒有內建刪除快取的機制(早期Nginx免費版本可以使用proxy_cache_purgefastcgi_cache_purge命令來清除快取,只不過後來這功能要在商業版本上才能使用了),在沒有額外腳本或程式的幫助下,我們大概也只能用rm等Linux指令來手動刪除快取目錄中的檔案,十分不方便,而且有一定的危險性。



如果您不知道如何在Ubuntu Server架設Nginx伺服器,也不知道怎麼使用proxy_pass的快取功能的話,可以先參考這篇文章:

如果您是架設PHP網站的話,可以再參考這篇文章來了解fastcgi_pass的快取功能:

Nginx Cache Purge

「Nginx Cache Purge」是一個高效Nginx快取清除工具,是解決免費版本的Nginx不能使用proxy_cache_purgefastcgi_cache_purge命令的替代方案。

GitHub:

安裝和解除安裝Nginx Cache Purge

透過Cargo

如果系統環境中有安裝「Cargo」的話,可以直接使用以下指令來下載「Nginx Cache Purge」的原始碼專案,並進行編譯安裝。

cargo install nginx-cache-purge

若要解除安裝,執行以下這個指令即可:

cargo uninstall nginx-cache-purge
透過GitHub(適用於Linux x86_64)

執行以下指令,將最新的Nginx Cache Purge執行檔從GitHub上下載下來,並移動到/usr/local/bin目錄中。

curl -fL "$(curl -fsS https://api.github.com/repos/magiclen/nginx-cache-purge/releases/latest | sed -r -n 's/.*"browser_download_url": *"(.*\/nginx-cache-purge_'$(uname -m)')".*/\1/p')" -O && sudo mv nginx-cache-purge_$(uname -m) /usr/local/bin/nginx-cache-purge && sudo chmod +x /usr/local/bin/nginx-cache-purge

若要解除安裝,執行以下這個指令即可:

sudo rm /usr/local/bin/nginx-cache-purge

使用Nginx Cache Purge

命令列介面

Nginx Cache Purge有提供CLI(命令列介面),指令用法如下:

nginx-cache-purge p <CACHE_PATH> <LEVELS> <KEY>

CACHE_PATH是使用Nginx的proxy_cache_path或是fastcgi_cache_path命令時所設定的快取儲存目錄路徑。LEVELS是使用Nginx的proxy_cache_path或是fastcgi_cache_path命令時所設定的目錄和目錄檔名結構,格式為x[:y[:z]]KEY是使用Nginx的proxy_cache_key或是fastcgi_cache_key命令時所設定的快取鍵值。

建議把快取的鍵值以Nginx的$request_uri為結尾,因為Nginx Cache Purge還有Wildcard清除功能。KEY中可以使用星號*來表示任意數量的任意字元。

如果想避開清除到某些快取,可以使用一個或多個-e參數再加上要保留的快取的鍵值,同樣支援Wildcard匹配。

Nginx + Nginx Cache Purge
啟動Nginx Cache Purge服務

Nginx Cache Purge自帶HTTP服務,可以用來處理清除快取的請求。使用以下指令可以啟動該HTTP服務:

nginx-cache-purge s

以上指令會使Nginx Cache Purge啟動自帶的HTTP服務,並監聽/tmp/nginx-cache-purge.sock,這個是UDS (Unix Domain Socket)。

要讓Nginx Cache Purge的服務在Linux開機之後自動啟動,可以先參考以下這篇文章了解:

以下是參考用的Systemd Unit檔案和啟用指令:

[Unit]
Description=Nginx Cache Purge
After=network.target
 
[Service]
# same as the user/group of the nginx process
User=www-data
Group=www-data

ExecStart=/usr/local/bin/nginx-cache-purge start
Restart=always
RestartSec=3s
 
[Install]
WantedBy=multi-user.target
sudo systemctl daemon-reload
sudo systemctl start nginx-cache-purge
sudo systemctl status nginx-cache-purge

sudo systemctl enable nginx-cache-purge

有了Nginx Cache Purge服務後,就可以在Nginx設定檔中將HTTP請求導給Nginx Cache Purge服務處理。我們在指定的location區塊中,或是location區塊內的if區塊中,去呼叫Nginx Cache Purge的服務。

至於要在哪個location區塊中或是if區塊中去執行Nginx Cache Purge,其實也沒有一定的標準,大致上有兩種作法。

HTTP的PURGE請求方法

在有使用到proxy_cache或是fastcgi_cache命令的區塊中,可以去判斷客戶端的請求方法是不是PURGE。雖然PURGE請求方法並不在HTTP標準中,但許多人會用它來當作清除快取的請求方法,有點約定俗成的感覺。

若要用這個方式來清除快取,可以在/etc/nginx/conf.d目錄中,新增purge.conf檔案,檔案內容如下:

map $request_method $is_purge {
    default   0;
    PURGE     1;
}

以上的map命令,可以去判斷客戶端的請求方法是不是PURGE,如果是的話,$is_purge變數的值就是1,否則是0

接著就是在location區塊內使用if命令判斷$is_purge變數,並在if區塊中使用content_by_lua_block命令執行Lua腳本。假設Nginx Cache Purge的執行檔路徑是/usr/local/bin/nginx-cache-purge,設定方式如下:

http {
    ...

    map $request_method $is_purge {                                                             
        default   0;
        PURGE     1;
    }

    proxy_cache_path /tmp/cache levels=1:2 keys_zone=my_cache:10m;
    proxy_cache_key $scheme$request_uri;

    server {
        ...

        location / {
            if ($is_purge) {
                set $my_cache_key $scheme$request_uri;
            
                proxy_pass http://unix:/tmp/nginx-cache-purge.sock;
                
                rewrite ^ /?cache_path=/tmp/cache&levels=1:2&key=$my_cache_key break;
            }

            proxy_cache my_cache;
            proxy_pass upstream;
            include proxy_params;
        }
    }
}

不過當然,我們還需要加入自己的存取驗證機制,避免隨便一個陌生人都可以連到我們的伺服器上清除快取。

以上的設定若成功,

  • 發送PURGE /path/to/abc請求,表示要清除GET /path/to/abc產生的快取。
  • 發送PURGE /path/to/*請求,表示要清除GET /path/to/**/*產生的快取,像是GET /path/to/GET /path/to/abcGET /path/to/123/456?a=bc
  • 發送PURGE /path/to/*/foo/*/bar請求,表示要清除GET /path/to/**/foo/**/bar產生的快取,像是GET /path/to/123/foo/456/barGET /path/to/a/foo/b/c?e=bar
專門用來清除快取的location區塊

如果不想把清除快取的功能和proxy_cache或是fastcgi_cache命令放在同一個location區塊,並使用PURGE請求方法來切換功能。也是可以另外再建立出location區塊,設定一個專門用來清除快取的端點,不過快取鍵值中$request_uri的部份就會需要在服務的請求查詢中加上remove_first欄位來移除多出來的網址部件。

例如:

http {
    ...

    proxy_cache_path /tmp/cache levels=1:2 keys_zone=my_cache:10m;
    proxy_cache_key $scheme$request_uri;

    server {
        ...

        location / {
            proxy_pass upstream;
            include proxy_params;
        }

        location /purge/ {
            set $my_cache_key $scheme$request_uri;
            
            proxy_pass http://unix:/tmp/nginx-cache-purge.sock;
                
            rewrite ^ /?cache_path=/tmp/cache&levels=1:2&remove_first=/purge&key=$my_cache_key break;
        }
    }
}

如果HTTP服務有清除到快取,就會回傳200狀態碼;如果沒有要清除的快取,就會回傳202狀態碼。

讓某些快取避免被清除

在服務的請求查詢中加上一個或多個exclude_keys欄位,可以設定要排除的快取鍵值,同樣支援Wildcard匹配。

不要HTTP服務

我們可能會想要直接在Nginx中搭配lua-nginx-module來呼叫Nginx Cache Purge的CLI介面。那麼我們可以在編譯Nginx Cache Purge時關閉其service特色,以此得到更小的執行檔。

可以使用以下指令來安裝沒有自帶HTTP服務的Nginx Cache Purge:

cargo install nginx-cache-purge --no-default-features