最近的工作中,我需要在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);//这个函数将对内存结点的引用以及临时文件释放掉
}