ATS緩存概述

在volume.config裏面可以配置多個volume,比如說有4塊磁盤,配了3個volume。每塊磁盤都有3個volume,一個磁盤的一個volume是一個stripe,stripe是磁盤級別存儲的最小單位。stripe存着若干個存儲對象,默認每個對象最小佔8000字節(proxy.config.cache.min_average_object_size),每個存儲對象表示爲一個fragment,默認大小是1M(proxy.config.cache.target_fragment_size),每個對象最大佔一個fragment。

一個資源根據其大小可能會存在多個存儲對象中。如果足夠小,連同這個資源的meta信息一起存儲在一個doc中。如果比較大,第一個存儲對象保存資源的meta信息,後面跟着若干個fragment存着資源的content。

每個對象對應一個索引(directory),索引存儲在內存中,索引是緩存key的md5值,由dir_probe函數計算得到。一個stripe的存儲對象對應的directory數量可以用stripe大小除8000得到,每4個directory是一個bucket,一個segment最多有2的14次方個bucket。每個directory佔10字節,在ats啓動的時候就會根據磁盤的大小計算出最多可以存多少個fragment,也即最多多少個directory,根據directory的個數可以計算出他們佔的內存大小。directory包括了某個存儲對象在磁盤上的位置信息,其中最重要的是偏移量(offset)和“大概大小”(big, size)其結構如下:

wKiom1gFoWzxelyJAAE2VemXAxY827.png

緩存相關有幾個比較核心的結構體,每個結構中有幾個核心的成員。如下是簡單介紹:


class vol

len: stripe的大小
segments: stripe中segment的個數
buckets: segment中bucket的個數 
Queue<CacheVC, Continuation::Link_link> agg: 一個寫隊列,每個元素是一個CacheVC


struct OpenDirEntry包含了一個有效的資源的基本信息,struct OpenDir裏面有一個變量是bucket,是一個由OpenDirEntry組成的數據,記錄了bucket的讀寫操作
writing_vec: 目前有人在執行寫操作
reading_vec: 目前有人在執行讀操作


struct Doc

magic: 判斷document是否有效

total_len: 整個資源到當前的fragment爲止的大小,不包括響應頭

flen: fragment存的content的大小,再加上72字節就是整個fragment的大小(默認1048576,proxy.config.cache.target_fragment_size)

hlen: 頭的大小,整個資源的metadata的大小

key: 當前fragment的某一個alternate的key,經常被用來與CacheVC::key做比較,CacheVC::key貌似是當前正在處理的key

first_key: 一個資源可能對應多個fragment,first_key是第一個fragment的key,同一個資源多個fragment有相同的first_key

len:fragment的數據大小

對於小文件:len = hlen + total_len + 72

doc->data():返回了相應body數據地址


class CacheVC

seek_to:range的起始地址

offset:第一個fragment的偏移量+整個請求範圍發了的數據大小

doc_pos:這個fragment已經發送的遊標位置

key:當前fragment的目標alternate的key,第一個存響應體的fragment的key,也即earliest_key,如果資源佔了多個fragment,first_key和key不相同

first_key: 資源的第一個fragment的key,如果資源只包含在一個fragment中,這個字段貌似沒用

doc_len:當前資源的目標alternate的總大小,不包括響應頭

length:等待被寫入agg_buf的長度
frag_len:一個fragment的大小,不包括72字節


不得不提到幾個重要的宏:

設置某一個continuation的回調函數

SET_CONTINUATION_HANDLER(c, &CacheVC::openReadStartHead);

#define SET_CONTINUATION_HANDLER(_c,_h) \
  (_c->handler = ((ContinuationHandler) _h))

#endif


設置回調函數

SET_HANDLER(&CacheVC::handleReadDone);

#define SET_HANDLER(_h) \
  (handler = ((ContinuationHandler)_h))

#endif

函數執行只是修改了handler


設置暫時的回調函數,保存原來的回調函數

#define PUSH_HANDLER(_x) do {                                           \
    save_handler = handler; handler = (ContinuationHandler)(_x);        \

} while (0)

函數執行是一個push的操作,將原來的handler“推向”了save_handler,將_x“推向”了handler


恢復原來的回調函數

#define POP_HANDLER do {                                          \

    handler = save_handler;                                       \

} while (0)

函數執行是一個pop的操作,將save_handler“彈出”到handler,執行完了之後handler和save_handler指向相同的函數,原來的handler沒了


幾個dir相關的小函數

dir_segment

這個函數是一個宏定義#define dir_segment(_s, _d) vol_dir_segment(_d, _s)

vol_dir_segment(Vol *d, int s)
{
  return (Dir *) (((char *) d->dir) + (s * d->buckets) * DIR_DEPTH * SIZEOF_DIR);
}

vol_dir_segment的最後一個參數是這個vol中segment的序號

vol的dir的地址 + segment的序號 * 一個bucket中dir的數量(默認4個) * 一個dir的長度(默認10字節),最後返回的是segment的第一個dir


dir_inssd(_e)
這個函數可以判斷dir是否存在於ssd中

#define dir_inssd(_e) (((_e)->w[4] >> 15) & 1)


dir_bucket

TS_INLINE Dir *
dir_bucket(int b, Dir *seg)

{
  return dir_in_seg(seg, b * DIR_DEPTH);
}

#define dir_in_seg(_s, _i) ((Dir*)(((char*)(_s)) + (SIZEOF_DIR *(_i))))

segment的第一個dir + vol中bucket的序號 * 一個bucket中dir的數量(默認4個) * 一個dir的長度(默認10字節),最後返回的是bucket的第一個dir


vol_offset

#define vol_offset(d, e)    ((d)->start + (off_t) ((off_t)dir_offset(e) * CACHE_BLOCK_SIZE) - CACHE_BLOCK_SIZE)


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章