Nginx-cache manager process进程分析

整理:flyingbird

                               Nginx-cache manager process进程分析


Nginx中缓存过期队列定时清理的实现,是通过event_timer红黑树来实现的。


一、相关数据结构先行


struct ngx_event_s {

    …...

    ngx_event_handler_pt  handler;   //事件处理函数

    …...

    ngx_rbtree_node_t   timer;  //包含有超时时间的红黑树节点
}


二、源码剖析


nginx中对cache的管理是通过一个单独的进程cache manager process进行的,下面直接对源码进行分析。

1. 事件处理方法注册(src/os/unix/ngx_process_cycle.c)


static ngx_cache_manager_ctx_t  ngx_cache_manager_ctx = {
    ngx_cache_manager_process_handler, "cache manager process", 0
};

//注册ngx_event_handler_pt为ngx_cache_manager_process_handler

//后面会将给方法赋值给event-handler


2.启动cache manager 进程


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);

    …...

}

3.cache manager进程执行方法体:

static void
ngx_cache_manager_process_cycle(ngx_cycle_t *cycle, void *data)
{
    ……

    //将ngx_cache_manager_ctx_t-handler赋值给event-handler,

    //这里ngx_cache_manager_ctx_t-handler的赋值是在1处进行的

    ev.handler = ctx->handler;


     …...

    for ( ;; ) {
       …
         //处理事件和超时
        ngx_process_events_and_timers(cycle);
    }
}

ngx_event.c
//处理超时和事件
void
ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
    .........
    if (delta) {
        //查找timer红黑树,如果有超时的节点,则执行节点对应的event-handler方法
        ngx_event_expire_timers();
    }
    …
}
  

4. 遍历timer红黑树,并执行相应过期节点的回调函数


static void
ngx_cache_manager_process_handler(ngx_event_t *ev)
{
    …

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

        if (path[i]->manager) {

             //实际上就是ngx_http_file_cache_manager,

            //在ngx_http_file_cache_set_slot函数中完成的manager的注册

            n = path[i]->manager(path[i]->data);

            next = (n <= next) ? n : next;

            ngx_time_update();
        }

    }


    if (next == 0) {
        next = 1;
    }

    ngx_add_timer(ev, next * 1000);
}

5. 超时事件处理函数ngx_cache_manager_process_handler
 
代码位于src/http/ngx_http_file_cache.c,"proxy_cache_path"指令的set()函数

char *
ngx_http_file_cache_set_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
    …......
    cache->path->manager = ngx_http_file_cache_manager;//注册manager
    cache->path->loader = ngx_http_file_cache_loader;

    cache->path->data = cache;

    ........

}


ngx_http_file_cache.c
//遍历缓存过期队列,从而删除过期的缓存

static time_t
ngx_http_file_cache_manager(void *data)
{
    ........
    //先删除过期的缓存
    next = ngx_http_file_cache_expire(cache);
    cache->last = ngx_current_msec;
    cache->files = 0;
    for ( ;; ) {
        ngx_shmtx_lock(&cache->shpool->mutex);
        //获取删除过期缓存后的缓存队列的大小
        size = cache->sh->size;
        ngx_shmtx_unlock(&cache->shpool->mutex);
        ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
                       "http file cache size: %O", size);
         //如果空间在指定范围内,不用再删了。return         
        //如果size超过磁盘的使用空间,即size >= cache->max_size 
        //强制把部分缓存删除,以保证缓存使用的空间在指定范围内                                 
        if (size < cache->max_size) {
            return next;

        }


        wait = ngx_http_file_cache_forced_expire(cache);

        if (wait > 0) {
            return wait;
        }

        if (ngx_quit || ngx_terminate) {
            return next;
        }
    }
}

//用于清理cache的缓存队列中过期的节点对应的缓存数据
static time_t
ngx_http_file_cache_expire(ngx_http_file_cache_t *cache)
{
    ........
    path = cache->path;
    //len表示缓存文件对应的全路径名称的长度

    /* 全路径的名称形式为:proxy_cache_path+'/'+根据level生成的子路径+16进制表示的

    MD5码,path->name.len + 1 :表示proxy_cache_path+'/'的长度,path->len 表示根

    据 level生成的子路径的长度,2 * NGX_HTTP_CACHE_KEY_LEN 表示16进制表示的

    MD5码的长度,之所以是2 * NGX_HTTP_CACHE_KEY_LEN 是因此MD5码是16个字

    节,一个字节是8位,而一个16进制数字只需要4位表示,因此MD5码所占的位数为

    16*8,转换成16进制的形式,所表示的字节的个数为16*8/2=16*2 = 

                                                                           NGX_HTTP_CACHE_KEY_LEN * 2

     */


    len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN;
    name = ngx_alloc(len + 1, ngx_cycle->log);

    if (name == NULL) {
        return 10;
    }

    ngx_memcpy(name, path->name.data, path->name.len);
    now = ngx_time();

    ngx_shmtx_lock(&cache->shpool->mutex);

    for ( ;; ) {

        if (ngx_queue_empty(&cache->sh->queue)) {
            wait = 10;
            break;
        }

        //取得过期队列的最后一个节点
        q = ngx_queue_last(&cache->sh->queue);
        //获得过期队列节点对应的ngx_http_file_cache_node_t节点的地址
        fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
        wait = fcn->expire - now;

        //表示当前的cache文件没有过期,则直接跳出循环,

        //因为过期队列越是最新的就越靠前存放,最新的缓存存在队列头部


        if (wait > 0) {
            wait = wait > 10 ? 10 : wait;
            break;
        }

        //取得过期队列的最后一个节点
       q = ngx_queue_last(&cache->sh->queue);

        //获得过期队列节点对应的ngx_http_file_cache_node_t节点的地址
        fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue);
        wait = fcn->expire - now;

        //表示当前的cache文件没有过期,则直接跳出循环,

        //因为过期队列越是最新的就越靠前存放,最新的缓存存在队列头部


        if (wait > 0) {
            wait = wait > 10 ? 10 : wait;
            break;
        }
        ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0,
                       "http file cache expire: #%d %d %02xd%02xd%02xd%02xd",
                       fcn->count, fcn->exists,
                       fcn->key[0], fcn->key[1], fcn->key[2], fcn->key[3]);

        if (fcn->count == 0) {
                                          //删除磁盘中缓存的文件
            ngx_http_file_cache_delete(cache, q, name);
            continue;
        }

        if (fcn->deleting) {
            wait = 1;
            break;
        }

        //将node中字符表示的MD5码,key转换为16进制表示的MD5码
        //并将转换后的16进制表示形式存储在key中,方法执行完后
        //返回转换后的字符串的最后一个字符
        p = ngx_hex_dump(key, (u_char *) &fcn->node.key,
                         sizeof(ngx_rbtree_key_t));
        len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t);
        //将fcn->key转换成16进制表示的形式

        //fcn->key中存储的是url的MD5码的后12个字符


        (void) ngx_hex_dump(p, fcn->key, len);

        //通过上面的两部转换,就将URL的MD5吗转换成了

        //16进制表示的形式并且存储在了key中

        /*
         * abnormally exited workers may leave locked cache entries,
         * and although it may be safe to remove them completely,
         * we prefer to remove them from inactive queue and rbtree
         * only, and to allow other leaks

         */

        ngx_queue_remove(q);
        ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node);
        ....
    }
    ......
}
         //取得过期队列的最后一个节点
        q =ngx_queue_last(&cache->sh->queue);
         //获得过期队列节点对应的ngx_http_file_cache_node_t节点的地址
        fcn = ngx_queue_data(q,ngx_http_file_cache_node_t, queue);
        wait = fcn->expire - now;

三、小结


综上所述:nginx通过timer红黑树完成过期缓存定时清理的工作流程为:

1. 首先注册超时处理函数:ngx_cache_manager_process_handler和缓存管理函数ngx_http_file_cache_manager。


2. 在cache manager执行进程中进入无限循环,反复调用ngx_process_events_and_timers方法。


3. 在ngx_process_events_and_timers方法中,调用ngx_event_expire_timers方法。


4. 在ngx_event_expire_timers方法中,找到超时的节点,并执行相应event中的handler。在超时时间中的handler就是前面注册的ngx_cache_manager_process_handler。


5. 在ngx_cache_manager_process_handler方法中,遍历ngx_cycle中的所有path,并执行每个path的manger回到函数,在cache中的path的manager回调函数就是前面注册的ngx_http_file_cache_manager。因此在ngx_cache_manager_process_handler中会执行ngx_http_file_cache_manager函数。


6. 在ngx_http_file_cache_manager中完成对缓存过期队列的遍历,并删除过期缓存占用的内存(红黑节点、过期队列节点等)。


(全文完)

你可能感兴趣的:(Nginx-cache manager process进程分析)