squid原始碼分析2—refresh_pattern機制分析
阿新 • • 發佈:2019-02-07
1、refresh_pattern機制分析概述
你可以在配置檔案裡放置任意數量的refresh_pattern行。squid按順序查詢它們以匹配正則表示式。當squid找到一個匹配時,它使用相應的值來決定,某個快取響應是存活還是過期。refresh_pattern語法如下:
refresh_pattern [-i] regexp min percent max [options]
例如:
refresh_pattern -i \.jpg$ 30 50% 4320 reload-into-ims
refresh_pattern -i \.png$ 30 50% 4320 reload-into-ims
refresh_pattern -i \.htm$ 0 20% 1440
refresh_pattern -i \.html$ 0 20% 1440
refresh_pattern -i . 5 25% 2880
regexp引數是大小寫敏感的正則表示式。你可以使用-i選項來使它們大小寫不敏感。squid按順序來檢查refresh_pattern行;當正則表示式之一匹配URI時,它停止搜尋。
min引數是分鐘數量。它是過時響應的最低時間限制。如果某個響應駐留在cache裡的時間沒有超過這個最低限制,那麼它不會過期。類似的,max引數是存活響應的最高時間限制。如果某個響應駐留在cache裡的時間高於這個最高限制,那麼它必須被重新整理。
在最低和最高時間限制之間的響應,會面對squid的最後修改係數(LM-factor)演算法。對這樣的響應,squid計算響應的年齡和最後修改係數,然後將它作為百分比值進行比較。響應年齡簡單的就是從原始伺服器產生,或最後一次驗證響應後,經歷的時間數量。源年齡在Last-Modified和Date頭部之間是不同的。LM-factor是響應年齡與源年齡的比率。
圖7-2論證了LM-factor演算法。squid快取了某個目標3個小時(基於Date和Last-Modified頭部)。LM-factor的值是50%,響應在接下來的1.5個小時裡是存活的,在這之後,目標會過期並被當作過時處理。假如使用者在存活期間請求cache目標,squid返回沒有確認的cache命中。若在過時期間發生請求,squid轉發確認請求到原始伺服器。
圖: 基於LM-factor計算過期時間
理解squid檢查不同值的順序非常重要。如下是squid的refresh_pattern演算法的簡單描述:
假如響應年齡超過refresh_pattern的max值,該響應過期;
假如LM-factor少於refresh_pattern百分比值,該響應存活;
假如響應年齡少於refresh_pattern的min值,該響應存活;
其他情況下,響應過期。
refresh_pattern指令也有少數選項導致squid違背HTTP協議規範。它們如下:
override-expire
該選項導致squid在檢查Expires頭部之前,先檢查min值。這樣,一個非零的min時間讓squid返回一個未確認的cache命中,即使該響應準備過期。
override-lastmod
改選項導致squid在檢查LM-factor百分比之前先檢查min值。
reload-into-ims
該選項讓squid在確認請求裡,以no-cache指令傳送一個請求。換句話說,squid在轉發請求之前,對該請求增加一個If-Modified-Since頭部。注意這點僅僅在目標有Last-Modified時間戳時才能工作。外面進來的請求保留no-cache指令,以便它到達原始伺服器。
ignore-reload
該選項導致squid忽略請求裡的任何no-cache指令。
2.1關鍵資料結構
refresh_t
2009 struct _refresh_t {
2010 const char *pattern;
2011 regex_t compiled_pattern;
2012 time_t min;
2013 double pct;
2014 time_t max;
2015 refresh_t *next;
2016 struct {
2017 unsigned int icase:1;
2018 #if HTTP_VIOLATIONS
2019 unsigned int override_expire:1;
2020 unsigned int override_lastmod:1;
2021 unsigned int reload_into_ims:1;
2022 unsigned int ignore_reload:1;
2023 unsigned int ignore_no_cache:1;
2024 unsigned int ignore_private:1;
2025 unsigned int ignore_auth:1;
2026 #endif
2027 unsigned int ignore_stale_while_revalidate:1;
2028 } flags;
2029 int max_stale;
2030 int stale_while_revalidate;
2031 int negative_ttl;
2032 };
stale_flags
56 typedef struct
57 {
58 unsigned int expires: // 是否有過期時間的標記
59 1;
60 unsigned int min: // 匹配到MIN標記
61 1;
62 unsigned int lmfactor: // 匹配到百分比標記
63 1;
64 unsigned int max; // 匹配到MAX標記
65 } stale_flags;
Refresh_pattern的檢查時機位於clientCacheHit()函式中的refreshCheckHTTPStale()函式中。該函式的流程為在disk中找到obj,Hit後即開始檢查時間,看是否可以用這個Obj進行回覆。
// 過期檢查refresh_pattern stale==0時為Hit stale = refreshCheckHTTPStale(e, r) {
int reason = refreshCheck(entry, request, -Config.refresh_stale_window)
{
if (check_time > entry->timestamp)
age = check_time - entry->timestamp;//檢查時間-時間戳
// 搜尋匹配的R規則
R = uri ? refreshLimits(uri) : refreshUncompiledPattern("."); if (NULL == R) R = &DefaultRefresh; // 檢查是否過期,這裡描述了max\ percentage\min三個值的比較先後順序 staleness = refreshStaleness(entry, check_time, age, R, &sf) {
if (entry->expires > -1)
{ // 資料有過期時間標識則用時間來做是否過期的判斷依據 if (entry->expires > check_time)//沒過期
return -1
else
return (check_time - entry->expires);//過期了
}
// 比較max的值,如果大於直接過期
if (age > R->max)
return (age - R->max);
// 比較時間戳,如果小於時間戳直接過期
if (check_time < entry->timestamp)
return (entry->timestamp - check_time);
// 比較百分比,有最後修改時間
if (entry->lastmod > -1 && entry->timestamp > entry->lastmod)
{
ime_t stale_age = (entry->timestamp - entry->lastmod) * R->pct;
//依據此時間做比較
if (age >= stale_age)
return (age - stale_age); // 直接過期
else
return -1; // 不過期
}
// 比較Min
if (age < R->min)
return -1;//不過期
else
return (age - R->min); //過期
}
if (staleness < 0)
return 不過期標誌
// 得到過期的細分標誌
.....
// 不過期 }
if (reason == STALE_WITHIN_DELTA)
return -1;
if (reason == STALE_ASYNC_REFRESH)
return -2;
if (reason == STALE_MAX_STALE)
return 3;
return (reason < 200) ? 0 : 1; }
// 過期處理
if (stale)
{
........
// refresh pattern 檢查
clientRefreshCheck(http);
return;
}
// Hit流程
clientProcessHit(http)
refresh_pattern檢查耗時的操作
refresh_pattern檢查耗時的操作在函式refreshLimits(const char *url)中執行,該函式將遍歷Config.Refresh進行正則匹配操作。具體程式碼如下:
118 const refresh_t *
119 refreshLimits(const char *url)
120 {
121 const refresh_t *R;
122 for (R = Config.Refresh; R; R = R->next)
123 {
124 if (!regexec(&(R->compiled_pattern), url, 0, 0, 0))
125 return R;
126 }
127 return NULL;
128 }
可見如果每個正則的pattern很寬鬆,並且正好匹配到的正則在連結串列的最末尾,則耗時最長。