Nginx 的 open_file_cache 相關配置可以緩存靜態文件的元信息,在這些靜態文件被頻繁訪問時可以顯着提升性能。
被緩存的文件元信息包括:
- fd,文件被打開一次后,fd保留使用
- size
- path
- last modified time
- …
這里有個配置示例:
open_file_cache max=64 inactive=30d; open_file_cache_min_uses 8; open_file_cache_valid 3m;
max=64 表示設置緩存文件的最大數目為 64, 超過此數字后 Nginx 將按照 LRU 原則丟棄冷數據。
inactive=30d 與 open_file_cache_min_uses 8 表示如果在 30 天內某文件被訪問的次數低於 8 次,那就將它從緩存中刪除。
open_file_cache_valid 3m 表示每 3 分鍾檢查一次緩存中的文件元信息是否是最新的,如果不是則更新之。
2 為什么只緩存文件元信息而不緩存文件內容?
這個問題的關鍵是 sendfile(2).
Nginx 在 serve 靜態文件的時候用的是 sendfile(2), 當然前提是你配置了 sendfile on, sendfile(2) 直接在 kernel space 內傳輸數據,對比使用 read(2)/write(2) 省去了兩次 kernel space 與 user space 之間的數據拷貝。而同時這些被頻繁讀取的靜態文件的內容會被 OS 緩存到 kernel space。在這樣的機制下,我們緩存中有文件的 fd 和 size,直接調用 sendfile(2) 就可以了。
如果要 Nginx 連內容一起緩存,那就需要每次文件變化都要用 read(2) 將數據從 kernel space 復制到 user space,然后放在 user space,每次應答請求的時候再從 user space 復制到 kernel space 然后寫入 socket。比起前面的方式,這樣的方式毫無優點。
3 在文件緩存更新周期內文件發生變化了會發生什么?
上面提到的配置中,30 天無訪問丟棄,每 3 分鍾做一次信息有效性監測,我們暫且把 3 分鍾叫做緩存更新周期。那在這 3 分鍾之內文件發生變化了會怎樣呢?
3.1 文件被刪除
由於 nginx 還持有原文件的 fd,所以你刪除此文件后,文件並不會真正消失, client 還是能通過原路徑訪問此文件。即便你刪除后又新建了一個同名文件,在當前緩存更新周期內能訪問到的還是原文件的內容。
3.2 文件內容被修改
文件內容被修改可以分為兩種情況:
- 文件大小不變或增大
-
由於 nginx 緩存了文件的 size 並且使用 這個緩存中 size 調用 sendfile(2),所以此種情況的后果是:
- 從文件開始到原 size 字節中的變化可以被 client 看到。
- 原 size 之后的內容不會被 sendfile(2) 發送,因此 client 看不到此部份內容。
- 文件大小減小
- 此種情況下,由於同樣原因,nginx 在 HTTP Header 中告訴 client 文件大小還是原來的尺寸,而 sendfile(2) 只能發送真正的文件數據,長度小於 HTTP Header 中設置的大小,所以 client 會等待到自己超時或者 Nginx 在 epoll_wait 超時后關閉連接。
4 如何設置?
- 如果你的靜態文件內容變化頻繁並且對時效性要求較高,一般應該把
open_file_cache_valid設置的小一些,以便及時檢測和更新。 - 如果變化相當不頻繁的話,那就可以設置大一點,在變化后用 reload nginx 的方式來強制更新緩存。
- 對靜態文件訪問的 error 和 access log 不關心的話,可以關閉已提升效率。
