一个资源根据其大小可能会存在多个存储对象中。如果足够小(连同doc结构的大小小于一个fragment的size),连同这个资源的meta信息一起存储在一个doc中。如果比较大,第一个存储对象保存资源的meta信息,后面跟着若干个fragment存着资源的content。这里讨论小文件读行为,并且内存命中,在资源第二次命中的时候才有可能是内存命中。整个流程在主状态机流程的入口函数是Cache::open_read,正流程如下:
Cache::open_read: 计算的到vol。运行了dir_probe函数,确定了缓存查询的结果。获取了dir,对CacheVC对象的key和dir进行了初始化。生成一个CacheVC对象,之后的操作都是这个CacheVC对象是事儿了。设置回调函数为Cache::openReadStartHead,设置完回调函数会执行do_read_call,最终返回了EVENT_RETURN,执行了事件回调函数,就是刚刚设置的Cache::openReadStartHead。
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函数。内存命中的话在这个函数里面就已经获取到了doc。
CacheVC::openReadStartHead: 每次读cache操作都会执行一次,获取这个资源相关的信息。确定一个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
CacheVC::openReadMain: CacheVC::openReadStartEarliest函数之后进入这个函数,为了找到第一个fragment的偏移量,在while (i > 1)中完成。计算整个请求当前尚未响应的数据大小,当前fragment尚未发送的数据。