nginx缓存映射在内存中的结点的生命周期

最近的工作中,我需要在nginx缓存映射在内存中的结点的数据结构(ngx_http_file_cache_node_t)内添加自定义字段(ngx_queue)。为了添加这个字段,就必须弄明白这些内存结点的生存周期,从生成到消亡;否则,新加字段在生成的时候无法初始化,后续的自定义队列结点也无法正确插入。

 

    1.内存结点的生成

到目前为此,我仅仅找到了两个生成内存结点的入口函数,可能还有漏掉的。在节点生成的时刻,我需要对结点中自定义的字段完成初始化。

      (生成点1)

       如果启用了proxy cache功能, master process会在启动的时候启动管理缓存的两个

子进程(cache manager process, cache loader process)。其中,后者仅仅在最初启动后的60s

内存在,它的作用是将定期磁盘中的缓存映射到内存中,这个任务通过多次调用ngx_cache_loader

_process_handler(ngx_process_cycle.c)来实现。这个函数主要调用了ngx_http_file_cache_

loader(void *data)

static void

ngx_http_file_cache_loader(void *data) {

……

//初始化tree

tree.init_handler = NULL;

tree.file_handler = ngx_http_file_cache_manager_file;//找到缓存文件的情况下,建立内存结点

tree.pre_tree_handler = ngx_http_file_cache_noop;

tree.post_tree_handler = ngx_http_file_cache_noop;

tree.spec_handler = ngx_http_file_cache_delete_file;//内存结点建立失败后,删除磁盘上的缓存???

if (ngx_walk_tree(&tree,&cache_path_name) == NGX_ABORT) {}//ngx_walk_tree完成缓存

//到内存映射的主要功能

}

ngx_int_t

ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t *tree) {

……

for(;;) {

…….

if (ngx_de_is_file($dir)) {

ctx->file_handler(ctx,$file);//这里调用了ngx_http_file_cache_manager_file函数

}

}

}

在ngx_http_file_cache中,

static ngx_int_t

ngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path) {

….

if (ngx_http_file_cache_add_file(ctx,path) != NGX_OK) {

(void) ngx_http_file_cache_delete_file(ctx,path);//如果没有成功映射到内存,就把缓存文件删除???

}

return ngx_http_file_cache_manager_sleep(cache);//如果当前服务器io资源紧张,就把该进程挂起一段时间

}

在ngx_http_file_cache.c中,

static ngx_int_t

ngx_http_file_cache_add_file() {

……

return ngx_http_file_cache_add(cache, &c);

}

在ngx_http_file_cache.c中,

static ngx_init_t

ngx_http_file_cache_add(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) {//生成点在此函数

….

fcn = ngx_http_file_cache_lookup(cache, c->key);//在内存中查找

if (fcn == NULL) {//如果内存中没有找到,则建立结点

fcn -> uses = 1;//结点初始化,我自定义的内容也应当在此处初始化

fcn -> count = 0;

fcn -> exists = 1;

….

}

}

        (生成点2)

在客户端请求到来后,nginx会主动检索内存结点,如不存在则建立(无论磁盘缓存是否存在)。

static ngx_int_t

ngx_thhp_file_cache_exists(ngx_http_file_cache_t *cache, ngx_http_cache_t *c) {

fcn = ngx_http_file_cache_lookup(cache,c -> key);

if (fcn) {

fcn -> uses++;

fcn -> count++;

……

}

//如果执行到这里,说明结点不存在

fcn = ngx_slab_alloc_locked()//新建结点
//在这里完成结点中自定义结构的初始化

}

       2. 内存结点的注销

我需要在结点注销的同时,释放自定义字段申请的空间。由于我仅仅需要在使用结点

完成自定义的功能,所以我只关心,如何才能让结点在我使用完后才被系统回收。据我简单地

观察,似乎只有在fcn->count == 0的情况下,系统才会调用.所以,在完成自定义的功能前,

先执行fcn -> count++,在完成任务后,再执行fcn->count–,就可以避免该结点被其他进程清理

掉。

3. 内存结点参与的一些行为

我需要了解在客户端请求资源到nginx进行相应之间,内存结点参与的一些行为。因为我

将在其中合适的地方实现自己需要的功能。

(1)查询内存结点是否存在

在ngx_http_file_cache.c中,

static ngx_int_t

ngx_http_file_cache_exists(ngx_http_file_open *cache, ngx_http_cache_t *c) {

……

fcn = ngx_http_file_cache_lookup(cache,c->key);

if (fcn) {

fcn -> uses++;//uses似乎表示该结点被使用过多少次。

fcn -> count++;//count似乎用来表示该结点当前被多少个(单位?进程?)使用

//可以通过这个字段来防止该结点被其它结点删除,例如,如果在mutux锁

//解锁后依然不希望该结点被删除,需要fcn -> count++;使用完结点后

//需要fcn->count–;

……

}

//如果执行到这里,说明结点不存在。

fcn = ngx_slab_alloc_locked() //新建结点

fcn -> uses = 1;

fcn -> count = 1;

done:

c->node = fcn;

}

(2)判断是否需要从源服务器更新或是添加结点对应的缓存

在ngx_http_file_cache.c中,

ngx_int_t

ngx_http_file_cache_open() {

rc = ngx_http_file_cache_exists(cache.c);

cold = cache->sh->cold;

if (rc == NGX_OK) {//结点已经存在

test = c->exists?1:0//如果缓存不存在,则需要联系源服务器

rv  = NGX_DECLINED;//即使内存结点表示缓存存在,如果无法成功打开磁盘上的存文件

//也需要联系源服务器

} else {//内存结点刚被创立或是出错

if(c -> min_uses > 1) {//不清楚这一分支代表了什么情况

if (!cold) {                        //可能是如果c -> min_uses > 1而当前必然有c->uses == 1,所以

}                                       //暂时不更新缓存。

test = 1;

rv = NGX_HTTP_CACHE_SCARCE;

} else {

c -> temp_file = 1;

test = cold?1:0;//如果cold == 0,就联系源服务器,实在不明白,这个cold是什么意思???

rv = NGX_DECLINED;

}

}

if (!test) {

return DECLINED;// 更新点一(代码执行到此处,就必然会联系源服务器更新数据)

}

 

if(ngx_open_cached_file(dlcf->open_file_cache,&c->file.name,&c->of,r->pool) != NGX_OK) {

switch(of.err) {

case …..

case NGX_ENOENT:

case NGX_ENOTDIR:

return rv;;// 更新点二,(代码执行到此处,如果rv==NGX_DECLIDE ,就必然会联系源服务器更新数据)

}

}

……

return  ngx_http_file_cache_read(r,c);

}

在ngx_http_file_cache.c中,

static ngx_int_t

ngx_http_file_cache_read(ngx_http_request *r, ngx_http_cache_t *c) {

……

if (h->crc32 != c->crc32) {

return NGX_DECLINED;//更新点三,不过在实际情况中,很少执行至此

}

……

if (c->valid_sec < now) {

if (c->node->updating) {

rc = NGX_HTTP_CACHE_UPDATING; //更新点四,缓存已过期,但尚未更新完毕

} else {

rc = NGX_HTTP_CACHE_STALE;//更新点五,缓存已过期

}

}

}

  (3) 如果无需更新缓存,则直接回应客户端

在ngx_http_upstream.c中,

static mgx_int_t

ngx_http_upstream_cache() {

……

switch (rc) {

case NGX_OK:

rc = ngx_http_upstream_cache_send(r,u);

 

}

}

static ngx_int_t

ngx_http_upstream_cache_send() {

……

if (rc == NGX_OK) {

if (ngx_http_upstream_process_headers(r,u) != NGX_OK) {

return NGX_DONE

}

return ngx_http_cache_send(r);//将已存在的缓存发回客户端

}

}

 

ngx_http_upstream_process_headers(){

if (…..) {

ngx_http_upstream_finalize_request();

}

}

ngx_http_upstream_finalize_request() {

……

ngx_http_file_cache_free(r->cache,u->pipe->temp_file);//这个函数将对内存结点的引用以及临时文件释放掉

}