1. 程式人生 > >squid原始碼分析2—refresh_pattern機制分析

squid原始碼分析2—refresh_pattern機制分析

1、refresh_pattern機制分析概述

    refresh_pattern指令間接的控制磁碟快取。它幫助squid決定,是否某個給定請求是cache命中,或作為cache丟失對待。寬鬆的設定增加了你的cache命中率,但也增加了使用者接收過時響應的機會。另一方面,保守的設定,降低了cache命中率和過時響應。refresh_pattern規則僅僅應用到沒有明確過時期限的響應。原始伺服器能使用Expires頭部,或者Cache-Control:max-age指令來指定過時期限。
你可以在配置檔案裡放置任意數量的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、原始碼分析
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;

2.2refresh_pattern檢查時機

   Refresh_pattern的檢查時機位於clientCacheHit()函式中的refreshCheckHTTPStale()函式中。該函式的流程為在disk中找到obj,Hit後即開始檢查時間,看是否可以用這個Obj進行回覆。

2.3refresh_pattern檢查流程
// 過期檢查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很寬鬆,並且正好匹配到的正則在連結串列的最末尾,則耗時最長。