使用Nginx作為緩存伺服器(Cache Server)

緩存伺服器 (Cache Server)

緩存伺服器是用來減輕server loading/traffic的, 怎麼說呢?因為他會將使用者對server的請求結果, 緩存在自己身上; 這樣一來我們的server就不需要對於同樣的請求一直回覆, 因為緩存伺服器會代替我們回覆!


架構解說

第一次請求: 因為CacheServer上面沒資料, 所以回源獲取資料
User  -->  Cache Server  -->  Origin Server
User  <--  Cache Server  <--  Origin Server

第二次請求: CacheServer上面已經有緩存了, 直接代理回應
User  -->  Cache Server    Origin Server
User  <--  Cache Server    Origin Server

實際配置

環境介紹

這次的lab我們會用兩台ubuntu來示範, 兩台都會安裝nginx
proxy.demosite.com: 緩存伺服器的域名
origin.demosite.com: 源站的域名

緩存伺服器配置

首先編輯/etc/nginx/conf.d/cache.conf, 這是全域的快取配置, 內容如下

proxy_cache_path /tmp/nginx/cache levels=1:2 keys_zone=myzone:10m inactive=1d max_size=10g;
proxy_cache_key '$scheme$host$request_uri';

接著編輯/etc/nginx/sites-enabled/proxy.conf 起一個新的site, 內容如下

server {
    listen 80;

    server_name proxy.demosite.com;

    location ~ .*\.(html|png)$ {
        proxy_cache myzone;
        proxy_cache_valid  any 100m;
        proxy_pass http://origin.demosite.com;
    }

    location /info {
        proxy_cache myzone;
        expires -1;

        proxy_pass http://origin.demosite.com;
    }

    add_header X-Cache-Status $upstream_cache_status;
}

配置的緩存路徑資料夾要存在嘿: mkdir -p /tmp/nginx/cache
配置完成就可以重新載入配置文件nginx -s reload

源伺服器配置

源伺服器不需要做其他額外的配置啦~


配置解說

這邊解釋所使用到的基本配置參數, 更完整的內容請參閱Module ngx_http_proxy_module

全域快取配置

先看到上面/etc/nginx/conf.d/cache.conf這個檔案裡面的內容
proxy_cache_path: 緩存路徑, 要把緩存的內容放在哪裡
levels=1:2: 緩存的目錄結構
keys_zone=myzone:10m: zone的名字和可使用記憶體大小, 配至1MB的zone大約可以存8000筆鍵值的快取
inactive=1d: 如果一天之內沒人存取, 就會從自己身上刪除啦
max_size=10g: 允許在身上存放的硬碟容量
proxy_cache_key '$scheme$host$request_uri$is_args$args';: 每筆快取儲存的鍵值方式

virtual site配置

這配置就是/etc/nginx/sites-enabled/proxy.conf
proxy_cache myzone;: 代表要使用剛剛定義的zone
proxy_cache_valid any 100m;: 對於任何結果緩存100分鐘
expires -1;: 過期時間 -1, 就是不緩存啦~
add_header X-Cache-Status $upstream_cache_status;: 增加回應的header, 這樣可以清楚知道有沒有中快取


驗證測試

重新載入nginx之後, 就可以來測試我們的緩存狀況囉~

觀察緩存路徑

先看下我們的緩存路徑, 目前空空如也

root@ubuntu-87:/etc/nginx/conf.d# tree /tmp/nginx/cache/
/tmp/nginx/cache/

0 directories, 0 files

首次請求緩存伺服器

接著對緩存伺服器做一次請求看看

❯ curl -v proxy.demosite.com
* Rebuilt URL to: proxy.demosite.com/
*   Trying 192.168.41.87...
* TCP_NODELAY set
* Connected to proxy.demosite.com (192.168.41.87) port 80 (#0)
> GET / HTTP/1.1
> Host: proxy.demosite.com
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 200 OK
< Server: nginx/1.10.3 (Ubuntu)
< Date: Mon, 21 Jan 2019 08:57:37 GMT
< Content-Type: text/html
< Content-Length: 334
< Connection: keep-alive
< Last-Modified: Tue, 15 Jan 2019 04:20:55 GMT
< ETag: "5c3d5fa7-14e"
< X-Cache-Status: MISS
< Accept-Ranges: bytes
<
<html>
  <body>
  this is 41.166
  gun.png
  <img src="gun.jpg" width="50px" height="50px">
  <br>

  about/about.png
  <img src="about/about.png" width="50px" height="50px">
  <br>

  info/info.png
  <img src="info/info.png" width="50px" height="50px">
  <br>

  <a href="about">about</a>
  <a href="info">info</a>
  </body>
</html>
* Connection #0 to host proxy.demosite.com left intact

大家注意看到這時候表頭多了這個X-Cache-Status: MISS
代表

  1. 我們的緩存配置生效囉
  2. MISS代表身上沒緩存, 因此回源獲取資料; 反之則為HIT

再次查看緩存路徑

經過請求之後, 我們再次查看緩存路徑. 已經有一筆資料囉!
而這個目錄結構就是我們上面配置的level=1:2

root@ubuntu-87:/etc/nginx/conf.d# tree /tmp/nginx/cache/
/tmp/nginx/cache/
└── 3
    └── 51
        └── 600a83b12cb107e844ddd14077759513

第二次請求緩存伺服器

第二次存取的時候因為緩存伺服器身上已經有資料了, 所以就得到HIT囉!

❯ curl -v "proxy.demosite.com" 2>&1 | grep X-Cache-Status
< X-Cache-Status: HIT

緩存解說 proxy_cache_key

這部分對於緩存的正確性以及後續的清理緩存還是比較關鍵的, 因此拉出來解釋一下.

鍵值意義

對於每一筆請求, 我們的緩存伺服器是如何知道自己身上有沒有緩存呢? 沒錯就是看proxy_cache_key
還記得上面我們配置proxy_cache_key '$scheme$host$request_uri$is_args$args';

以我們剛剛的請求來說, proxy.demosite.com , 產生的鍵值就會是 httpproxy.demosite.com/

(http)  proxy.demosite.com (/)
$scheme $host              $request_uri $is_args$args

我們可以看一下剛才產生的緩存文件, 這個快取文件就是完整的html檔案, 裡面就說明了他的鍵值
KEY: httpproxy.demosite.com/

root@ubuntu-87:/etc/nginx/conf.d# cat /tmp/nginx/cache/3/51/600a83b12cb107e844ddd14077759513
0E\_=\E\G"5c3d5fa7-14e"
KEY: httpproxy.demosite.com/
HTTP/1.1 200 OK
Server: nginx/1.10.3 (Ubuntu)
Date: Mon, 21 Jan 2019 09:07:12 GMT
Content-Type: text/html
Content-Length: 334
Last-Modified: Tue, 15 Jan 2019 04:20:55 GMT
Connection: close
ETag: "5c3d5fa7-14e"
Accept-Ranges: bytes

<html>
  <body>
  this is 41.166
  gun.png
  <img src="gun.jpg" width="50px" height="50px">
  <br>

  about/about.png
  <img src="about/about.png" width="50px" height="50px">
  <br>

  info/info.png
  <img src="info/info.png" width="50px" height="50px">
  <br>

  <a href="about">about</a>
  <a href="info">info</a>
  </body>
</html>

所以 只要使用者請求的Key不同, nginx就可以生成不同的快取文件
換句話說, 如果您的網站下面這兩個請求應該得到不同的結果,

  • proxy.demosite.com?name=foo
  • proxy.demosite.com?name=bar
    您對於proxy_cache_key的定義就必須精細到arg, 否則nginx會忽略存取參數; 一直以為是同樣的一份文件唷!

而如果您的網站http/https可以不用視為兩份緩存, 那key就可以省略scheme這個變數囉!

鍵值計算

那麼md5怎麼來的呢, 就是使用Key得到的; 我們可以驗證看看

❯ echo -n 'httpproxy.demosite.com/' | md5
600a83b12cb107e844ddd14077759513

清理緩存

所以說, 配置了緩存就要可以清理緩存; 否則有時候網站有更新, 使用者還是會持續看到舊的資料.

方法一: 刪除整個目錄

此方法最為直觀簡易啦~ 整個緩存全部清除, 也不用想太多
rm -rf /tmp/nginx/cache/*
nginx -s reload

優點: 快速簡易
缺點: 無法控制要刪除的項目

這個方法適用於網站單純的情況下, 不需要考慮如果緩存伺服器突然失去所有檔案, 進而突發回源造成大量存取

方法二: 刪除指定目錄/域名

什麼是刪除指定目錄呢? 比如我們今天想刪除 /info 下面所有的緩存
或是比如我們的緩存伺服器同時有兩個網站, 而我只想對單一網站進行緩存清理; 就可以使用這個方法囉!

  1. 首先我們理解我們的緩存檔案裡面都會存放我們定義的鍵值
  2. 找出鍵值符合的檔案
  3. 批量刪除

先看一下; 目前我的緩存共有四筆, 其中的鍵值也都取出來觀察

root@ubuntu-87:/etc/nginx/conf.d# tree /tmp/nginx/cache/
/tmp/nginx/cache/
├── 3
│   ├── 51
│   │   └── 600a83b12cb107e844ddd14077759513
│   └── 9b
│       └── 3ace4f08b744df7310abeb90109e19b3
├── 5
│   └── d8
│       └── 80af67f39abf128943025ab5e1498d85
└── c
    └── 19
        └── 53e313c4a7c020ab414089c28c4c419c


root@ubuntu-87:/etc/nginx/conf.d# grep -ar "KEY: httpproxy.demosit
e.com/" /tmp/nginx/cache
/tmp/nginx/cache/3/51/600a83b12cb107e844ddd14077759513:KEY: httpproxy.demosite.com/
/tmp/nginx/cache/3/9b/3ace4f08b744df7310abeb90109e19b3:KEY: httpproxy.demosite.com/about/about.png
/tmp/nginx/cache/5/d8/80af67f39abf128943025ab5e1498d85:KEY: httpproxy.demosite.com/info/info.png
/tmp/nginx/cache/c/19/53e313c4a7c020ab414089c28c4c419c:KEY: httpproxy.demosite.com/info/info.html

四筆鍵值記錄分別是

  • httpproxy.demosite.com/
  • httpproxy.demosite.com/about/about.png
  • httpproxy.demosite.com/info/info.png
  • httpproxy.demosite.com/info/info.html

實際操作: 清除 /info 下面的所有緩存
grep -alr "KEY: httpproxy.demosite.com/info" /tmp/nginx/cache | xargs rm


這時候再看一下我們的緩存目錄, 該目錄之下的緩存都清除完畢囉!

root@ubuntu-87:/etc/nginx/conf.d# tree /tmp/nginx/cache/
/tmp/nginx/cache/
├── 3
│   ├── 51
│   │   └── 600a83b12cb107e844ddd14077759513
│   └── 9b
│       └── 3ace4f08b744df7310abeb90109e19b3
├── 5
│   └── d8
└── c
    └── 19

結語

我們下次見啦~