nginx -cache loader process进程分析

原文链接http://blog.csdn.net/lvxin_1986/article/details/7744079


                                                                                    cache loader process进程分析


本文主要对nginx的cache loader process进程进行分析,并进行记录。


一、揭开cache loader process神秘面纱


在nginx启动1分钟之后,会启动一个名为cache loader process的进程,该进程运行了一段时间之后,该进程就会自动退出。


在该进程运行期间主要做了以下事情:遍历配置文件中proxy_cache_path命令指定的路径中的所有的缓存文件,并且针对遍历到的各个缓存文件的MD5编码先遍历红黑树和相应的ngx_http_file_cache_node_t节点,如果不存在就创建新的ngx_http_file_cache_node_t,并将该对象中的rbnode和queue分别插入到红黑树和过期队列中;如果存在,则更新相应的属性。


通过上述操作,完成根据缓存文件进行索引数据的重建工作。


二、刨根究底


下面对该过程进行详细研究:

1.  结构先行


(1)ngx_path_t:保存了当前cache的一些信息,包含managerloader函数指针和传入数据地址。


typedef struct {

    ngx_str_t                             name;

    size_t                                  len;

    size_t                                  level[3];

    ngx_path_manager_pt        manager;

    //cache loader的函数指针,在运行cache loader进程的时候

  //通过该函数完成索引元数据的重建。

    ngx_path_loader_pt            loader; 

    void                                   *data;

    u_char                              *conf_file;

    ngx_uint_t                          line;

} ngx_path_t;


(2)ngx_http_file_cache_node_t:一个缓存数据对应一个ngx_http_file_cache_node_t结构。


typedef struct {

    ngx_rbtree_node_t            node;//缓存文件对应的红黑树节点

    ngx_queue_t                     queue;//队列

    //cache_key 12位=16位cache_key-4位rbt_key

    u_char                             key[NGX_HTTP_CACHE_KEY_LEN

                                               -sizeof(ngx_rbtree_key_t)];

    unsigned                         count:20; //引用计数

    unsigned                         uses:10;//多少请求在使用

    unsigned                         valid_msec:10;

    unsigned                         error:10;//状态

    unsigned                         exists:1;//是否存在对应的cache文件

    unsigned                         updating:1;//是否在更新

                                          /* 12unused bits */

    ngx_file_uniq_t               uniq;//文件的uniq

    time_t                             expire;//失效时间

    time_t                             valid_sec;//max-age?

    size_t                             body_start;//body 起始位置

    off_t                               fs_size;//文件大小

} ngx_http_file_cache_node_t;


(3)ngx_cache_manager_ctx_t

typedef struct {

   ngx_event_handler_pt      handler;    //上下文执行方法

   char                                *name;

   ngx_msec_t                     delay;     //延迟多长时间执行handler

}ngx_cache_manager_ctx_t;

 

2. 代码分析


(1)首先初始化缓存管理上下文:

   //ngx_cache_loader_ctx会用来注册超时事件对象,然后加入到事件和超时树中.

   //ngx_cache_loader_process_handler是超时的处理方法,60000是设定的超时时间,    //表示60000毫秒=60秒=1分钟.


static ngx_cache_manager_ctx_t  ngx_cache_loader_ctx = {

   ngx_cache_loader_process_handler, "cache loader process",60000

};


(2)在master执行代码中创建cache 管理进程

void

ngx_master_process_cycle(ngx_cycle_t *cycle)

{

    …..

    …..

    //启动worker进程

    ngx_start_worker_processes(cycle,ccf->worker_processes,  

                                                         NGX_PROCESS_RESPAWN);

    //启动cache manager进程

    ngx_start_cache_manager_processes(cycle,0);

    …..

    …..

}

(3)启动cache loader进程

static void

ngx_start_cache_manager_processes(ngx_cycle_t*cycle, ngx_uint_t respawn)

{

      …..

      …..

      //启动cache manager 进程

    //ngx_cache_manager_process_cycle是进程执行方法体, ngx_cache_manager_ctx

    //是方法体的传入参数

    //ngx_cache_manager_ctx是ngx_cache_manager_ctx-ngx_event_handler_pt将

    //会赋值给event-handler用于处理定时事件

    ngx_spawn_process(cycle, ngx_cache_manager_process_cycle,

                     &ngx_cache_manager_ctx, "cache manager process",

                      respawn ?NGX_PROCESS_JUST_RESPAWN :     

                                      NGX_PROCESS_RESPAWN);

 

    ch.command = NGX_CMD_OPEN_CHANNEL;

    ch.pid = ngx_processes[ngx_process_slot].pid;

    ch.slot = ngx_process_slot;

    ch.fd = ngx_processes[ngx_process_slot].channel[0];

    ngx_pass_open_channel(cycle, &ch);

    if(loader == 0) {

       return;

    }


    //启动cache loader进程,该进程的作用就是根据缓存文件在内存中重建索引元数据

    //该进程实际上就是执行ngx_cache_manager_process_cycle(cycle,  

    //                                                                             ngx_cache_loader_ctx);


    ngx_spawn_process(cycle, ngx_cache_manager_process_cycle,

                      &ngx_cache_loader_ctx, "cacheloader process",

                      respawn ? NGX_PROCESS_JUST_SPAWN :

                                       NGX_PROCESS_NORESPAWN);


   ch.command = NGX_CMD_OPEN_CHANNEL;

   ch.pid = ngx_processes[ngx_process_slot].pid;

   ch.slot = ngx_process_slot;

   ch.fd = ngx_processes[ngx_process_slot].channel[0];

   ngx_pass_open_channel(cycle, &ch);

}


(4)ngx_cache_manager_process_cycle函数分析


这是cache loader进程的执行体,如果启动的是cache loader ,data传递的是ngx_cache_loader_ctx。


static void

ngx_cache_manager_process_cycle(ngx_cycle_t*cycle, void *data)

{

    ngx_cache_manager_ctx_t *ctx = data;

    //将ngx_cache_manager_ctx_t->handler或者

    //ngx_cache_loader_ctx_t->handler赋值给event-handler。

    ev.handler = ctx->handler;

    ev.data = ident;

    ev.log = cycle->log;

    ident[3] = (void *) -1;

    ngx_use_accept_mutex = 0;

    ngx_setproctitle(ctx->name);

    //将ev中的timer加入到timer红黑树中,其中timer的类型是ngx_rbtree_node_t

    ngx_add_timer(&ev, ctx->delay);

    //进入无尽的循环,不停的处理事件和超时

    for ( ;; ) {

       if (ngx_terminate || ngx_quit) {

           ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "exiting");

           exit(0);

       }

       if (ngx_reopen) {

           ngx_reopen = 0;

           ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0, "reopeninglogs");

           ngx_reopen_files(cycle, -1);

       }

       //处理事件和超时

       ngx_process_events_and_timers(cycle);

    }

}

 

(5)再看ctx定义


ngx_cache_loader_ctx会用来注册超时事件对象,然后加入到事件和超时树中,ngx_cache_loader_process_handler是超时的处理方法,60000是设定的超时时间,表示60000毫秒=60秒=1分钟。


static ngx_cache_manager_ctx_t  ngx_cache_loader_ctx = {

   ngx_cache_loader_process_handler, "cache loader process",60000

};


可以看出,1分钟之后会执行ngx_cache_loader_process_handler方法,再看看该方法里面到底做了什么。


(6)ngx_cache_loader_process_handler方法分析

 

该方法同时是注册在事件和超时红黑树节点中事件的处理方法。


static void

ngx_cache_loader_process_handler(ngx_event_t*ev)

{

    ngx_uint_t     i;

    ngx_path_t  **path;

    ngx_cycle_t   *cycle;

 

    cycle = (ngx_cycle_t *) ngx_cycle;

 

    path = cycle->pathes.elts;

    for (i = 0; i < cycle->pathes.nelts;i++) {

 

        if (ngx_terminate || ngx_quit) {

            break;

        }

        //cache path中的loader在ngx_http_file_cache_set_slot方法中完成的注册,

        //并指向函数ngx_http_file_cache_loader。

        if (path[i]->loader) {

           path[i]->loader(path[i]->data);

            ngx_time_update();

        }

    } 

    exit(0);

}

 

(7)ngx_http_file_cache_loader函数分析


这是cache loader进程中主要的处理方法,该方法主要根据磁盘上的缓存文件来重建缓存元数据。该方法在ngx_http_file_cache_set_slot方法中进行注册,并在ngx_cache_loader_process_handler方法中进行依次调用,每个缓存路径都要注册一个该cache loader 方法。


static void 

ngx_http_file_cache_loader( void *data)

{

    ngx_http_file_cache_t  *cache = data;

    ngx_tree_ctx_t  tree;

    //进入loading状态

    if (!cache->sh->cold || cache->sh->loading) {

        return;

    }

    if(!ngx_atomic_cmp_set(&cache->sh->loading, 0, ngx_pid)) {

        return;

    }

    ngx_log_debug0(NGX_LOG_DEBUG_HTTP,ngx_cycle->log, 0,

                               "http file cacheloader");

    //初始化tree

    tree.init_handler = NULL;

    //注册该方法用于添加缓存文件的索引元数据

    tree.file_handler =ngx_http_file_cache_manage_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;  //注册删除缓存文件方法

    //上述的注册方法都会在ngx_walk_tree方法中进行调用

    tree.data = cache;

    tree.alloc = 0;

    tree.log = ngx_cycle->log;

    cache->last = ngx_current_msec;

    cache->files = 0;

    /*

ngx_walk_tree是递归函数,打开每层路径(dir)直到每个文件(file),根据其路径和文 件名得到key,在缓存的rbtree(红黑树)里面找这个key(部分),如果没有找到的话,就在内存中分配一个映射这个文件的node(但是不会把文件的内容进行缓存),然后插入到红黑树中和加入队列。    

              ctx->file_handler=> 

              ngx_http_file_cache_manage_file=> 

              ngx_http_file_cache_add_file=> 

              ngx_http_file_cache_add 

              从n = ngx_read_file(...)函数可以看出,每个磁盘缓存文件的开头的 sizeof(ngx_http_file_cache_header_t)个byte存放了跟缓存相关的信息 

    */

    if (ngx_walk_tree(&tree,&cache->path->name) == NGX_ABORT) {

        cache->sh->loading = 0;

        return;

    }

    cache->sh->cold = 0;

    cache->sh->loading = 0;

    ngx_log_error(NGX_LOG_NOTICE,ngx_cycle->log, 0,

                  "http file cache: %V%.3fM, bsize: %uz",

                  &cache->path->name,

                  ((double)cache->sh->size * cache->bsize) / (1024 * 1024),

                  cache->bsize);

}

 

(8)最后分析ngx_walk_tree方法

 

/*

 *ctx->init_handler() - see ctx->alloc

 *ctx->file_handler() - file handler

 *ctx->pre_tree_handler() - handler is called before entering directory

 *ctx->post_tree_handler() - handler is called after leaving directory

 *ctx->spec_handler() - special (socket, FIFO, etc.) file handler

 *

 *ctx->data - some data structure, it may be the same on all levels, or

 *    reallocated if ctx->alloc is nonzero

 *

 *ctx->alloc - a size of data structure that is allocated at every level

 *    and is initilialized by ctx->init_handler()

 *

 *ctx->log - a log

 *

 * onfatal (memory) error handler must return NGX_ABORT to stop walking tree

 */

/*

ngx_walk_tree是递归函数,打开每层路径(dir)直到每个文件(file),根据其路径和文件名得到key,在缓存的rbtree(红黑树)里面找这个key(部分),如果没有找到的话,就在内存中分配一个映射这个文件的node(但是不会把文件的内容进行缓存),然后插入到红黑树中和加入队列。    

              ctx->file_handler=> 

              ngx_http_file_cache_manage_file=> 

              ngx_http_file_cache_add_file=>      

              ngx_http_file_cache_add 

              从n = ngx_read_file(...)函数可以看出,每个磁盘缓存文件的开头的sizeof(ngx_http_file_cache_header_t)个byte存放了跟缓存相关的信息   

*/

 

ngx_int_t

ngx_walk_tree(ngx_tree_ctx_t *ctx, ngx_str_t*tree)

{

   void       *data, *prev;

   u_char     *p, *name;

   size_t      len;

   ngx_int_t   rc;

   ngx_err_t  err;

   ngx_str_t   file, buf;

   ngx_dir_t   dir;

   ngx_str_null(&buf);

   ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,

                   "walk tree\"%V\"", tree);

    //打开name指定的目录,并将返回值以DIR*的形式赋值给dir->dir后

    //面对该目录下所有文件的访问都可以通过dir->dir进行

    if(ngx_open_dir(tree, &dir) == NGX_ERROR) {

       ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,

                      ngx_open_dir_n "\"%s\" failed", tree->data);

       return NGX_ERROR;

    }

    prev = ctx->data;

    if(ctx->alloc) {

       data = ngx_alloc(ctx->alloc, ctx->log);

       if (data == NULL) {

           goto failed;

       }

       if (ctx->init_handler(data, prev) == NGX_ABORT) {

           goto failed;

       }

       ctx->data = data;

    } else {

       data = NULL;

   }

   for ( ;; ) {

       ngx_set_errno(0);

        //返回下一个目录进入点,并赋值给dir->de

       if (ngx_read_dir(&dir) == NGX_ERROR) {

           err = ngx_errno;

           if (err == NGX_ENOMOREFILES) {

                rc = NGX_OK;

           } else {

                ngx_log_error(NGX_LOG_CRIT,ctx->log, err,

                              ngx_read_dir_n" \"%s\" failed", tree->data);

                rc = NGX_ERROR;

           }

           goto done;

       }          

       len = ngx_de_namelen(&dir);

       name = ngx_de_name(&dir);

       ngx_log_debug2(NGX_LOG_DEBUG_CORE, ctx->log, 0,

                      "tree name%uz:\"%s\"", len, name);

       if (len == 1 && name[0] == '.') {

           continue;

       }

       if (len == 2 && name[0] == '.' && name[1] == '.') {

           continue;

       }

       file.len = tree->len + 1 + len;

       if (file.len + NGX_DIR_MASK_LEN > buf.len) {

           if (buf.len) {

                ngx_free(buf.data);

           }

           buf.len = tree->len + 1 + len + NGX_DIR_MASK_LEN;

           buf.data = ngx_alloc(buf.len + 1, ctx->log);

           if (buf.data == NULL) {

                goto failed;

           }

       }

       p = ngx_cpymem(buf.data, tree->data, tree->len);

       *p++ = '/';

       ngx_memcpy(p, name, len + 1);

       file.data = buf.data;

       ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,

                       "tree path \"%s\"",file.data);

       if (!dir.valid_info) {

           if (ngx_de_info(file.data, &dir) == NGX_FILE_ERROR) {

                ngx_log_error(NGX_LOG_CRIT,ctx->log, ngx_errno,

                              ngx_de_info_n" \"%s\" failed", file.data);

                continue;

           }

       }

       //dir->de指向的是文件

       if (ngx_de_is_file(&dir)) {

           ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,

                           "tree file\"%s\"", file.data);

           ctx->size = ngx_de_size(&dir);

           ctx->fs_size = ngx_de_fs_size(&dir);

           ctx->access = ngx_de_access(&dir);

           ctx->mtime = ngx_de_mtime(&dir);

           //进行索引重建

           if (ctx->file_handler(ctx, &file) == NGX_ABORT) {

                goto failed;

           }

       //dir->de指向的是目录

       } else if (ngx_de_is_dir(&dir)) {

           ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,

                           "tree enter dir\"%s\"", file.data);

           ctx->access = ngx_de_access(&dir);

           ctx->mtime = ngx_de_mtime(&dir);

           //其实在ngx_http_file_cache_loader方法中已经完成了注册,只是返回NGX_OK

           if (ctx->pre_tree_handler(ctx, &file) == NGX_ABORT) {

                goto failed;

           }

           //针对于子目录进行递归调用

           if (ngx_walk_tree(ctx, &file) == NGX_ABORT) {

                goto failed;

           }

           ctx->access = ngx_de_access(&dir);

           ctx->mtime = ngx_de_mtime(&dir);

           //其实在ngx_http_file_cache_loader方法中已经完成了注册,只是返回NGX_OK

           if (ctx->post_tree_handler(ctx, &file) == NGX_ABORT) {

                goto failed;

           }

       } else {

          ngx_log_debug1(NGX_LOG_DEBUG_CORE, ctx->log, 0,

                          "tree special\"%s\"", file.data);

           if (ctx->spec_handler(ctx, &file) == NGX_ABORT) {

                goto failed;

           }

       }

    } 

failed:

    rc= NGX_ABORT;

done:

    if(buf.len) {

       ngx_free(buf.data);

    }

    if(data) {

       ngx_free(data);

       ctx->data = prev;

    }

    if(ngx_close_dir(&dir) == NGX_ERROR) {

       ngx_log_error(NGX_LOG_CRIT, ctx->log, ngx_errno,

                      ngx_close_dir_n "\"%s\" failed", tree->data);

    }

   return rc;

}


(全文完)


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值