一个资源根据其大小可能会存在多个存储对象中。如果足够小(连同doc结构的大小小于一个fragment的size),连同这个资源的meta信息一起存储在一个doc中。如果比较大,第一个存储对象保存资源的meta信息,后面跟着若干个fragment存着资源的content。这里讨论小文件读行为,并且内存命中,在资源第二次命中的时候才有可能是内存命中。整个流程在主状态机流程的入口函数是Cache::open_read,正流程如下:
Cache::open_read: 首先需要计算的到vol,因为需要知道资源在哪个vol上才能确定是否存在,每个vol都是独立存在的。获取锁之后运行了dir_probe函数,确定了缓存查询的结果。获取了dir,对CacheVC对象的key和dir进行了初始化。生成一个CacheVC对象,之后的操作都是这个CacheVC对象是事儿了。设置回调函数为Cache::openReadStartHead,设置完回调函数会执行do_read_call,最终do_read_call返回了EVENT_RETURN,执行了事件回调函数,就是刚刚设置的Cache::openReadStartHead。
dir_probe: 通过md5值计算得到资源所在的segemnt和bucket,进而得到目标dir,如果dir是否“正确”,如果正确就认为命中了。
CacheVC::do_read_call: 初始化doc_pos为0。解析dir,获取fragment大概大小(dir_approx_size)。决定是否应该存在于ssd中(dir_inssd),如果已经是从ssd中读了,就肯定不写了。每个vol都会对应自己的ssd,每个vol都会维护一些历史数据用来判断热度相关的数据。最终执行CacheVC::handleRead,最终返回了EVENT_RETURN。
CacheVC::handleRead: 获取偏移量(dir_get_offset)。 在内存中查询(vol->ram_cache->get函数会根据records.config中的配置项proxy.config.cache.ram_cache.algorithm指向不同的函数)。假设内存查询函数为RamCacheLRU::get,详见下面解析。内存命中的话在这个函数里面会将内容放倒buf中,进而获取到了doc。
RamCacheLRU::get: 判断是否在内存中命中,如果命中了,在lru队列中删除并且在头部重新插入。
CacheVC::openReadStartHead: 每次读操作都会执行一次,获取这个资源相关的信息,根据buf是否为空判断是否命中。确定一个alternate,通过判断alternate的key是否就是doc的key来判断这个alternate是不是要找的那个。如果是的话判断是不是single fragment,如果满足len = heln + total_len + 72,认为是小文件。获取doc_pos,即为doc的大小与整个资源的metadata的大小的和。获取next key(对于小文件没必要,但是也执行了),执行begin_read,这个函数主要判断了是否需要evacuate,由于小文件都会存在一个fragment,而此时已经都到了内存中,所以不需要考虑evacuate问题。最后将回调函数设置为 CacheVC::openReadMain,并且执行缓存主状态机事件回调函数 HttpCacheSM::state_cache_open_read,传递的参数是 CACHE_EVENT_OPEN_READ。
转载于:https://blog.51cto.com/11490450/1861921