在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)