nginx cache 學習總結
Nginx cache 學習總結
一、nginx cache的基本指令
1.1 proxy_cache
syntax: |
proxy_cache zone| off; |
default: |
proxy_cache off; |
context: |
http, server, location |
定義一塊共享記憶體區域用來進行快取。相同名稱的快取區域可以在多個地方使用。Off引數關閉從前一個級別配置中繼承下來的快取設定
1.2 proxy_cache_bypass
yntax: |
proxy_cache_bypass string…; |
default: |
— |
context: |
http, server, location |
定義一個條件,在這個條件成立後將不會從快取中獲取資料。至少有一個字串引數不為空,並且是不等於“0”,則響應不會從快取中獲取。
proxy_cache_bypass $cookie_nocache $arg_nocache$arg_comment;
proxy_cache_bypass $http_pragma $http_authorization;
Can be used along with the proxy_no_cache directive.
1.3 proxy_cache_key
syntax: |
proxy_cache_key string; |
default: |
proxy_cache_key $scheme$proxy_host$request_uri; |
context: |
http, server, location |
給快取資料定義一個鍵,例如
proxy_cache_key “$host$request_uri $cookie_user”;
預設情況下,該指令的值的字串
proxy_cache_key $scheme$proxy_host$uri$is_args$args;
1.4 proxy_cache_lock
syntax: |
proxy_cache_lock on | off; |
default: |
proxy_cache_lock off; |
context: |
http, server, location |
This directive appeared in version 1.1.12.
當指令被指定時,根據 proxy_cache_key指令確定的若干個或得出相同響應元素若干個請求僅僅有一個能被傳遞給後端的代理伺服器(最終傳遞給應用伺服器)去生成響應內容,生成後響應內容新增到cache中, 其他請求從cache中獲取資料。其他請求將等待cache中有內容出現或者等待超時為止。
1.5 proxy_cache_lock_timeout
syntax: |
proxy_cache_lock_timeout time; |
default: |
proxy_cache_lock_timeout 5s; |
context: |
http, server, location |
This directive appeared in version 1.1.12.
1.6 proxy_cache_min_uses
syntax: |
proxy_cache_min_uses number; |
default: |
proxy_cache_min_uses 1; |
context: |
http, server, location |
設定請求發生多少次後,請求(響應內容)被快取
1.7 proxy_cache_path
syntax: |
proxy_cache_path path[levels=levels] keys_zone=name:size [inactive=time] |
default: |
— |
context: |
http |
設定快取記憶體的路徑和其他引數。快取資料儲存在檔案中。在快取記憶體中快取內容檔名以及對應的key都用MD5函式處理。Levels引數確定快取檔案的層級關係。例如下面的配置檔案
proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=one:10m;
快取後的檔名將如下:
/ data/nginx/cache/c/29/b7f54b2df7773722d382f4809d65029c
一個快取的響應,首先被寫入到一個臨時檔案,然後重新命名一個檔案。從版本0.8.9臨時檔案和快取可以放在不同的檔案系統,但在這種情況下,一個檔案複製在兩個檔案系統中發生,而不是廉價的重新命名操作。因此建議,對於任何給定的位置,快取目錄和proxy_temp_path指令所設定的臨時檔案都放在同一個檔案系統中。
此外,所有活躍的資料資訊包括其key都儲存在一個共享記憶體區域中,這個區域的名稱及大小等keys_zone指令的引數進行配置。被快取在記憶體中的資料在一定時間沒有被訪問後將變成不活躍,這些資料將被從記憶體中移除無論資料是否還是有效,不活躍時間由inative 引數設定。預設情況下inactive是10分鐘。
“快取記憶體管理器”程序將監控max_size引數設定最大快取大小,超過此大小時,它消除了最近最少使用的資料。
一分鐘後開始的指定“cache loader”程序被啟用,他將載入以前快取在檔案系統中的快取資料到記憶體中。一個載入包括在若干個迭代過程。在一個迭代過程中載入檔案數量小於loader_files 引數指定的數量(預設值100).除此以外迭代時間小於loader_threshold限制的值,預設值200毫秒。在兩個迭代中間有個短暫的暫停(載入的暫停,nginx其他工作沒有暫停),這個時間被loader_sleep引數所控制,預設值50毫秒。
1.8 proxy_cache_use_stale
syntax: |
proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 |
default: |
proxy_cache_use_stale off; |
context: |
http, server, location |
如果nginx同代理伺服器工作過程中發生一個錯誤,nginx可能使用一個陳舊的被快取的資料。這個指令決定在那種情況下使用這個功能。這個指令的引數會同proxy_next_upstream指令的引數相匹配。
此外,如果目前正在更新,更新引數允許使用過時的快取的響應。這可以最大限度地減少更新快取資料時,代理伺服器的訪問次數。
為了儘量減少當填充一個新快取元素時訪問代理伺服器次數過多的問題,可以使用proxy_cache_lock指令,來緩解。
1.9 proxy_cache_valid
syntax: |
proxy_cache_valid [code…] time; |
default: |
— |
context: |
http, server, location |
設定不同響應程式碼的快取時間,例如如下指令:
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
設定快取的響應程式碼為200和302時間為10分鐘,程式碼為404的響應快取時間為1分鐘。
如果只指定快取時間
proxy_cache_valid 5m;
然後只有200,301和302響應被快取。
此外,它可以被指定快取任何響應通過使用any引數:
proxy_cache_valid 200 302 10m;
proxy_cache_valid 301 1h;
proxy_cache_valid any 1m;
快取的引數也能通過響應頭直接設定。這個設定具有更高優先順序比通過快取指令設定的快取時間。“X-ACCEL-Expires”頭欄位以秒為單位設定響應的快取時間。0禁止快取響應。
如果一個值以字首@開始,將針對響應設定一個絕對過期時間(自1970年1月1日0時到達過期時間的絕對秒數)的快取。若是響應頭中沒有 “X-Accel-Expires” 欄位,則快取引數可以通過“Expires” or “Cache-Control”欄位進行設定。若是一個響應頭包括“Set-Cookie” 欄位,則這樣的響應將不被快取。這樣的一個或者多個響應頭欄位的處理可以通過proxy_ignore_headers 指令進行禁止。
1.10 proxy_no_cache
syntax: |
proxy_no_cache string…; |
default: |
— |
context: |
http, server, location |
定義條件下的反應將不會被儲存到快取記憶體。如果至少有一個值的字串引數不為空,不等於“0”,那麼響應將不會被儲存:
proxy_no_cache$ cookie_nocache$ arg_nocache$ arg_comment;
proxy_no_cache$ http_pragma HTTP_AUTHORIZATION;
可用於沿的proxy_cache_bypass指令。
定義一些條件,當響應滿足這些條件時將不被快取起來。在條件字串中至少有一個條件不為空或者0,符合這樣條件的響應才不會被快取。
proxy_no_cache $cookie_nocache $arg_nocache$arg_comment;
proxy_no_cache $http_pragma $http_authorization;
二、nginx cache的相關結構體
2.1 ngx_http_file_cache_s結構體
ngx_http_file_cache_sh_t和ngx_http_file_cache_s是用來管理cache內容的結構體,本身並不儲存cache的內容。
ngx_path_t 用來描述每個儲存檔案的目錄資訊
當在nginx.conf使用xxx_cache_path這個命令時,將初始化上述結構體,具體在函式ngx_http_file_cache_set_slot 中實現對應成員的初始化。
cache->path->manager= ngx_http_file_cache_manager;
cache->path->loader= ngx_http_file_cache_loader;
cache->path->data= cache;
cache->path->conf_file= cf->conf_file->file.name.data;
cache->path->line= cf->conf_file->line;
cache->loader_files= loader_files;
cache->loader_sleep= loader_sleep;
cache->loader_threshold= loader_threshold;
cache->shm_zone->init= ngx_http_file_cache_init;
cache->shm_zone->data= cache;
cache->inactive= inactive;
cache->max_size= max_size;
其中ngx_http_file_cache_init實現ngx_http_file_cache_sh_t,也就是共享記憶體的初始化。
其中所有的path都由ngx_cycle_t->paths集中管理,對應的函式是ngx_add_path,臨時目錄:響應資料先寫入臨時檔案,然後將其重新命名為快取檔案,因此推薦xxx_temp_path和cache目錄位於同一檔案系統。
2.2 ngx_http_file_cache_node_t結構體
ngx_http_file_cache_node_t結構體用於將快取檔案的內容儲存在共享記憶體中,以便多個worker程序使用。cache_node的管理演算法為lru演算法,即node查詢和儲存使用紅黑樹,超時管理使用佇列,具體請參看http://flychao88.iteye.com/blog/1977653。
2.3 ngx_http_file_cache_header_t結構體
ngx_http_file_cache_header_t結構體為包頭結構,用於表示檔案系統中的快取檔案的儲存格式,儲存快取檔案的相關資訊(修改時間、快取 key 的 crc32 值、和用於指明HTTP 響應包頭和包體在快取檔案中偏移位置的欄位等)。
檔案快取格式資訊
2.4 ngx_http_cache_t結構體
ngx_http_cache_t結構體用於表示cache的完整資訊,它儲存在ngx_http_request_s結構體中,即每一個http request當開啟cache功能之後,將通過ngx_http_cache_t來儲存對應的快取條目資訊。請求使用的快取 file_cache 、快取條目對應的快取節點資訊 node 、快取檔案 file 、key 值及其檢驗 crc32 等等,都臨時保存於ngx_http_cache_t (ngx_http_request_t->cache) 結構體中,這個結構體中的資訊量基本上相當於ngx_http_file_cache_header_t 和 ngx_http_file_cache_node_t 的總和。三、nginx cache的manger和loader程序工作原理
當配置解析完畢之後,就會進入程序初始化部分,在ngx_cache_manager_process_cycle函式中將啟動cache manger和cache loader兩個程序。
cache manger process的作用是用來定時刪除無用的cache檔案(引用計數為0),一般來說只有manger會刪除無用的cache(特殊情況,比如在loader中分配共享記憶體失敗可能會強制刪除一些cache, 或者說 loader的時候遇到一些特殊檔案)。
cache loader process的主要作用是遍歷cache目錄,然後載入一些沒有被載入的檔案(比如nginx重啟後,也就是上次遺留的檔案),或者說將cache檔案重新插入(因為刪除是使用LRU演算法)。
3.1 cache manger和cache loader程序的啟動
首先ngx_cache_manager_process_cycle 函式中呼叫ngx_start_cache_manager_processes函式,然後在ngx_start_cache_manager_processes中分別啟動cache manger和cache loader程序。
// cache manager process,啟動後立馬執行
staticngx_cache_manager_ctx_t ngx_cache_manager_ctx = {
ngx_cache_manager_process_handler,"cache manager process", 0
};
// cache loader process,啟動後一分鐘後執行
staticngx_cache_manager_ctx_t ngx_cache_loader_ctx = {
};
static void
ngx_start_cache_manager_processes(ngx_cycle_t*cycle, ngx_uint_t respawn)
{
//啟動cachemanager process
ngx_spawn_process(cycle,ngx_cache_manager_process_cycle,
&ngx_cache_manager_ctx, "cache manager process",
respawn ?NGX_PROCESS_JUST_RESPAWN : NGX_PROCESS_RESPAWN);
//啟動cacheloader process
ngx_spawn_process(cycle,ngx_cache_manager_process_cycle,
&ngx_cache_loader_ctx, "cache loader process",
respawn ?NGX_PROCESS_JUST_SPAWN : NGX_PROCESS_NORESPAWN);
}
ngx_cache_manager_process_cycle主要就是設定定時器,並分別將ctx->handler傳給ev。
static void
ngx_cache_manager_process_cycle(ngx_cycle_t*cycle, void *data)
{
//將ctx->handler傳給ev
ev.handler = ctx->handler;
ev.data = ident;
ev.log = cycle->log;
//設定定時器
ngx_add_timer(&ev, ctx->delay);
ngx_process_events_and_timers(cycle);
}
這裡的ctx->handler,分別是ngx_cache_manager_ctx的ngx_cache_manager_process_handler和ngx_cache_loader_ctx的ngx_cache_loader_process_handler。
static void
ngx_cache_manager_process_handler(ngx_event_t*ev)
{
//獲取path結構體
path = ngx_cycle->paths.elts;
//遍歷所有cache目錄
for (i = 0; i < ngx_cycle->paths.nelts; i++) {
if (path[i]->manager) {
//呼叫path結構體中的manager函式
n =path[i]->manager(path[i]->data);
//獲取下一次定時器時間
next = (n <= next) ? n : next;
}
}
ngx_add_timer(ev, next * 1000);
}
static void
ngx_cache_loader_process_handler(ngx_event_t*ev)
{
//獲取path結構體
path = cycle->paths.elts;
//遍歷所有cache目錄
for (i = 0; i < cycle->paths.nelts;i++) {
if (path[i]->loader) {
//呼叫path結構體中的loader函式
path[i]->loader(path[i]->data);
}
}
}
以上path[i]->manager和path[i]->loader兩個函式是在ngx_http_file_cache_set_slot設定的,
cache->path->manager= ngx_http_file_cache_manager;
cache->path->loader= ngx_http_file_cache_loader;
3.2 ngx_http_file_cache_manager的相關函式
static time_t
ngx_http_file_cache_manager(void*data)
{
//處理超時cache節點,將其刪除
next = ngx_http_file_cache_expire(cache);
for ( ;; ) {
//如果沒有超過相關的最大限制,直接返回
if (size < cache->max_size&& count < watermark) {
return next;
}
//遍歷所有cache節點,強制刪除沒有被引用的cache節點
wait =ngx_http_file_cache_forced_expire(cache);
}
}
ngx_http_file_cache_expire函式,這裡nginx使用了LRU,也就是佇列最尾端儲存的是最長時間沒有被使用的,並且這個函式返回的就是一個wait值。
static time_t
ngx_http_file_cache_expire(ngx_http_file_cache_t*cache)
{
now = ngx_time();
ngx_shmtx_lock(&cache->shpool->mutex);
for ( ;; ) {
//如果cache佇列為空,則直接退出返回
if(ngx_queue_empty(&cache->sh->queue)) {
wait = 10;
break;
}
//從最後一個開始
q =ngx_queue_last(&cache->sh->queue);
fcn = ngx_queue_data(q,ngx_http_file_cache_node_t, queue);
wait = fcn->expire - now;
//如果沒有超時,則退出
if (wait > 0) {
wait = wait > 10 ? 10 : wait;
break;
}
//如果引用計數為0,則刪除這個cache節點
if (fcn->count == 0) {
ngx_http_file_cache_delete(cache,q, name);
continue;
}
//如果當前節點正在刪除,則退出迴圈
if (fcn->deleting) {
wait = 1;
break;
}
//將當前節點放入佇列最前端
ngx_queue_remove(q);
fcn->expire = ngx_time() +cache->inactive;
ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
}
ngx_http_file_cache_forced_expire函式,就是強制刪除cache 節點,它的返回值也是wait time,它的遍歷也是從後到前的。
static time_t
ngx_http_file_cache_forced_expire(ngx_http_file_cache_t*cache)
{
wait = 10;
//刪除節點嘗試次數
tries = 20;
//遍歷佇列
for (q = ngx_queue_last(&cache->sh->queue);
q !=ngx_queue_sentinel(&cache->sh->queue);
q = ngx_queue_prev(q))
{
fcn = ngx_queue_data(q,ngx_http_file_cache_node_t, queue);
//如果引用計數為0則刪除cache
if (fcn->count == 0) {
ngx_http_file_cache_delete(cache,q, name);
wait = 0;
} else {
//否則嘗試20次
if (--tries) {
continue;
}
wait = 1;
}
break;
}
return wait;
}
3.3 ngx_http_file_cache_loader的相關函式
static void
ngx_http_file_cache_loader(void*data)
{
ngx_tree_ctx_t tree;
/*
設定回撥函式,其中
init_handler:初始化遍歷過程中的相關資料結構,與alloc配合使用,只要alloc被賦值了,那麼init_handler也必須進行賦值,即一句話,要麼同時存在,要麼同時為空;
file_handler:處理普通檔案的回撥函式;
pre_tree_handler:進入一個目錄前的回撥函式;
post_tree_handler:離開一個目錄後的回撥函式;
spec_handler:處理特殊檔案的回撥函式,比如socket,FIFO等;
*/
tree.init_handler = NULL;
//對於每個cache檔案呼叫這個回撥
tree.file_handler = ngx_http_file_cache_manage_file;
//主要用來遍歷所有cache的目錄
tree.pre_tree_handler =ngx_http_file_cache_manage_directory;
tree.post_tree_handler =ngx_http_file_cache_noop;
tree.spec_handler =ngx_http_file_cache_delete_file;
//回撥資料為cache
tree.data = cache;
tree.alloc = 0;
tree.log = ngx_cycle->log;
//最後load的時間
cache->last = ngx_current_msec;
cache->files = 0;
//目錄樹遍歷
if (ngx_walk_tree(&tree,&cache->path->name) == NGX_ABORT) {
cache->sh->loading = 0;
return;
}
cache->sh->cold = 0;
cache->sh->loading = 0;
}
ngx_walk_tree函式,nginx目錄樹的遍歷
ngx_int_t
ngx_walk_tree(ngx_tree_ctx_t*ctx, ngx_str_t *tree)
{
//開啟需要遍歷的目錄
if (ngx_open_dir(tree, &dir) ==NGX_ERROR) {
ngx_log_error(NGX_LOG_CRIT,ctx->log, ngx_errno,
ngx_open_dir_n "\"%s\" failed", tree->data);
return NGX_ERROR;
}
//開始遍歷
for ( ;; ) {
//讀取當前目錄下的目錄項
if (ngx_read_dir(&dir) ==NGX_ERROR) {
err = ngx_errno;
goto done;
}
//獲取目錄項的名稱和長度
len = ngx_de_namelen(&dir);
name = ngx_de_name(&dir);
//構造新目錄項的完整路徑
file.len = tree->len + 1 + len;
p = ngx_cpymem(buf.data, tree->data,tree->len);
*p++ = '/';
ngx_memcpy(p, name, len + 1);
/*如果新的目錄項是普通檔案,呼叫file_handler進行處理,如果是目錄,遞迴呼叫ngx_walk_tree,如果是特殊檔案,呼叫spec_handler進行處理*/
if (ngx_de_is_file(&dir)) {
if (ctx->file_handler(ctx,&file) == NGX_ABORT) {
goto failed;
}
} else if (ngx_de_is_dir(&dir)) {
rc = ctx->pre_tree_handler(ctx,&file);
if (ngx_walk_tree(ctx, &file)== NGX_ABORT) {
goto failed;
}
if (ctx->post_tree_handler(ctx,&file) == NGX_ABORT) {
goto failed;
}
} else {
if (ctx->spec_handler(ctx, &file)== NGX_ABORT) {
goto failed;
}
}
}
failed:
rc = NGX_ABORT;
done:
//關閉檔案目錄
if (ngx_close_dir(&dir) == NGX_ERROR) {
ngx_log_error(NGX_LOG_CRIT,ctx->log, ngx_errno,
ngx_close_dir_n "\"%s\" failed", tree->data);
}
return rc;
}
ngx_http_file_cache_manage_file函式,用來載入cache檔案的控制代碼到記憶體中
static ngx_int_t
ngx_http_file_cache_manage_file(ngx_tree_ctx_t*ctx, ngx_str_t *path)
{
cache = ctx->data;
//將檔案新增進cache管理結構中
if (ngx_http_file_cache_add_file(ctx, path)!= NGX_OK) {
(void)ngx_http_file_cache_delete_file(ctx, path);
}
//如果檔案個數太大,則休眠並清理
if (++cache->files >=cache->loader_files) {
ngx_http_file_cache_loader_sleep(cache);
} else {
ngx_time_update();
//否則看loader時間是不是過長,如果過長則又進入休眠
if (elapsed >=cache->loader_threshold) {
ngx_http_file_cache_loader_sleep(cache);
}
}
return (ngx_quit || ngx_terminate) ?NGX_ABORT : NGX_OK;
}
ngx_http_file_cache_add_file函式,它主要是通過檔名計算hash,然後呼叫ngx_http_file_cache_add將這個檔案加入到cache管理中。
static ngx_int_t
ngx_http_file_cache_add(ngx_http_file_cache_t*cache, ngx_http_cache_t *c)
{
//查詢cache node
fcn = ngx_http_file_cache_lookup(cache,c->key);
if (fcn == NULL) {
//如果不在,則重新構建
fcn =ngx_slab_calloc_locked(cache->shpool,
sizeof(ngx_http_file_cache_node_t));
}
ngx_memcpy((u_char *)&fcn->node.key, c->key, sizeof(ngx_rbtree_key_t));
ngx_memcpy(fcn->key,&c->key[sizeof(ngx_rbtree_key_t)],
NGX_HTTP_CACHE_KEY_LEN -sizeof(ngx_rbtree_key_t));
//插入紅黑樹
ngx_rbtree_insert(&cache->sh->rbtree, &fcn->node);
fcn->uses = 1;
fcn->exists = 1;
fcn->fs_size = c->fs_size;
cache->sh->size += c->fs_size;
} else {
//否則刪除queue,後續會重新插入
ngx_queue_remove(&fcn->queue);
}
fcn->expire = ngx_time() +cache->inactive;
//重新插入到lru佇列的頭
ngx_queue_insert_head(&cache->sh->queue, &fcn->queue);
}
四、 nginx cache的應用過程
在http的請求傳送給有proxy_cache和proxy_pass定義的location的時候,會呼叫到ngx_http_proxy_handler,nginx向上遊伺服器傳送upstream請求時,會先檢查本地是否有可用快取。具體的位置是在ngx_http_upstream_init_request中,在向後端發起請求之前。
4.1 ngx_http_upstream_init_request函式
static void
ngx_http_upstream_init_request(ngx_http_request_t*r)
{
#if(NGX_HTTP_CACHE)
//檢查是否有配置proxy_cache,如果有則使用快取
if (u->conf->cache) {
ngx_int_t rc;
//尋找快取,如果返回NGX_DECLINED就是說快取中沒有,向後端傳送請求
if (rc == NGX_BUSY) {
//重試一次
r->write_event_handler =ngx_http_upstream_init_request;
return;
}
r->write_event_handler =ngx_http_request_empty_handler;
if (rc == NGX_OK) {
//快取找到了,分析快取檔案的結構體,讀取快取的headers+body,併發送給前端
if (rc == NGX_DONE) {
return;
}
if (rc ==NGX_HTTP_UPSTREAM_INVALID_HEADER) {
rc = NGX_DECLINED;
r->cached = 0;
}
}
if (rc != NGX_DECLINED) {
ngx_http_finalize_request(r, rc);
return;
}
}
#endif
//向後端發起請求
}
4.2 ngx_http_upstream_cache函式
所有請求快取使用的嘗試,都是通過 ngx_http_upstream_cache 函式開始的。該函式主要完成以下幾個功能。
(1)如果還未給當前請求分配快取相關結構體 ( ngx_http_cache_t ) 時,建立此型別欄位( r->cache ) 並初始化。
//獲得快取的管理結構體(ngx_http_file_cache_t)
ngx_http_upstream_cache_get(r, u,&cache)
//建立一個新的ngx_http_cache_t
ngx_http_file_cache_new(r)
//ngx_http_xxx_create_key 將 `xxx_cache_key`配置指令定義的快取 key 根據請求資訊進行取值
u->create_key(r)
//生成 md5sum(key) 和 crc32(key)並計算 `c->header_start` 值
ngx_http_file_cache_create_key(r)
//預設所有請求的響應結果都是可被快取的
u->cacheable = 1;
c = r->cache;
c->body_start = u->conf->buffer_size;
c->min_uses = u->conf->cache_min_uses;
c->file_cache = cache;
/*根據配置檔案中 ( xxx_cache_bypass ) 快取繞過條件和請求資訊,判斷是否應該
繼續嘗試使用快取資料響應該請求*/
ngx_http_test_predicates(r,u->conf->cache_bypass)
(2)呼叫ngx_http_file_cache_open 函式查詢是否有對應的有效快取資料,函式負責快取檔案定位、快取檔案開啟和校驗等操作。
ngx_int_t
ngx_http_file_cache_open(ngx_http_request_t*r)
{
cache = c->file_cache;
//第一次根據請求資訊生成的 key 查詢對應快取節點時,先註冊一下請求記憶體池級別的清理函式
if (c->node == NULL) {
cln = ngx_pool_cleanup_add(r->pool,0);
cln->handler =ngx_http_file_cache_cleanup;
cln->data = c;
}
//嘗試獲取快取檔案的節點資訊
rc = ngx_http_file_cache_exists(cache, c);
if (rc == NGX_ERROR) {
return rc;
}
if (rc == NGX_AGAIN) {
return NGX_HTTP_CACHE_SCARCE;
}
if (rc == NGX_OK) {
rv = NGX_DECLINED;
} else { /* rc == NGX_DECLINED */
//根據設定的最小min_uses決定是否對cache進行快取
if (c->min_uses > 1) {
rv = NGX_HTTP_CACHE_SCARCE;
} else {
rv = NGX_DECLINED;
}
}
//快取檔案完整檔名:r->cache->file.name.data,可以通過此檔名開啟快取
if (ngx_http_file_cache_name(r,cache->path) != NGX_OK) {
return NGX_ERROR;
}
//嘗試開啟並獲取檔案快取資訊
if(ngx_open_cached_file(clcf->open_file_cache, &c->file.name, &of,r->pool)
!= NGX_OK)
//建立用於儲存快取檔案頭的臨時緩衝區
c->buf = ngx_create_temp_buf(r->pool,c->body_start);
//讀取快取檔案頭並進行有效性驗證
return ngx_http_file_cache_read(r, c);
done:
return rv;
}
ngx_http_file_cache_open函式負責快取檔案定位、快取檔案開啟和校驗等操作,其返回值及對應含義,以及其呼叫者 ngx_http_upstream_cache 對應的行為總結如下:
NGX_OK
- 快取正常命中
- 設定 `cache_status` 為 `NGX_HTTP_CACHE_HIT`,然後向客戶端傳送快取內容
NGX_HTTP_CACHE_STALE
- 快取內容過期,當前請求需要向後端請求新的響應資料。
- 設定 `cache_status` 為 `NGX_HTTP_CACHE_EXPIRED`,並返回 `NGX_DECLINED`
以繼續請求處理 (`r->cached = 0; c->valid_sec = 0`)。
NGX_HTTP_CACHE_UPDATING
- 快取內容過期,同時己有同樣使用該快取節點的其它請求正在請求新的響應資料。
- 如果xxx_cache_use_stale 啟用了 "updating",設定 `cache_status` 為
`NGX_HTTP_CACHE_UPDATING`,然後向客戶端傳送過期快取內容。否則,將返回
值重設為 `NGX_HTTP_CACHE_STALE`。
NGX_HTTP_CACHE_SCARCE
- 因快取節點被查詢次數還未達 `min_uses`,對此請求禁用快取機制
- 繼續請求處理,但是不再快取其響應資料 (`u->cacheable = 0`)。
NGX_DECLINED
- 快取內容因為不存在 (`c->exists == 0`)、快取內容未通過校驗、或者當前請
求正在更新快取等原因,暫時無法使用快取。
- 繼續請求處理,並嘗試對其響應資料進行快取。
NGX_AGAIN
- 快取內容過期,並且當前快取節點正在被其它請求更新,或者 還未能從快取文
件中讀到足夠的資料 (aio 模組下)。
- 返回 `NGX_BUSY`,Nginx 會再次嘗試讀取快取。
NGX_ERROR
- 記憶體相關、檔案系統等系統錯誤。
- 返回 `NGX_ERROR`,Nginx 會呼叫 `ngx_http_finalize_request` 終止此請求。
NGX_HTTP_SPECIAL_RESPONSE
- 開啟 `xxx_intercept_errors` 配置情況下,直接返回快取的錯誤碼。
- 設定 `cache_status` 為 `NGX_HTTP_CACHE_HIT` 並返回錯誤碼。
(3)ngx_http_file_cache_exists函式
static ngx_int_t
ngx_http_file_cache_exists(ngx_http_file_cache_t*cache, ngx_http_cache_t *c)
{
fcn = c->node;
if (fcn == NULL) {
//以 c->key 為查詢條件從快取中查詢快取節點
fcn =ngx_http_file_cache_lookup(cache, c->key);
}
//如果找到了對應 c->key 的快取節點
if (fcn) {
//如果該請求第一次使用此快取節點,則增加相關引用和使用次數,繼續下面條件判斷
if (c->node == NULL) {
fcn->uses++;
fcn->count++;
}
//如果 xxx_cache_valid 配置指令對此節點過期時間做了特殊設定
if (fcn->error) {
//檢查節點是否過期
if (fcn->valid_sec <ngx_time()) {
//如果過期,重置節點,並返回 NGX_DECLINED
goto renew;
}
//如果未過期,返回NGX_OK
rc = NGX_OK;
goto done;
}
//如果快取檔案存在或者快取節點被使用次數超過 xxx_cache_min_uses配置值
if (fcn->exists || fcn->uses>= c->min_uses) {
c->exists = fcn->exists;
if (fcn->body_start) {
c->body_start =fcn->body_start;
}
rc = NGX_OK;
goto done;
}
//條件2, 3 都不滿足時,此次查詢失敗,返回NGX_AGAIN
rc = NGX_AGAIN;
goto done;
}
//未找到,建立新的cache node節點,並初始化
if (fcn == NULL) {
fcn =ngx_slab_calloc_locked(cache->shpool,
sizeof(ngx_http_file_cache_node_t));
}
cache->sh->count++;
ngx_memcpy((u_char *) &fcn->node.key,c->key, sizeof(ngx_rbtree_key_t));
ngx_memcpy(fcn->key,&c->key[sizeof(ngx_rbtree_key_t)],
NGX_HTTP_CACHE_KEY_LEN -sizeof