nginx - cache manage process 过期缓存定期清理的实现

来源:互联网 发布:小米软件商店 编辑:程序博客网 时间:2024/05/17 06:16

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

相关数据结构先行:

  

struct ngx_event_s {

ngx_event_handler_pt  handler;   //事件处理函数

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

}

 

  

 

 

 

 

 

 

 

 

 

 

 


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

1.事件处理方法注册

  

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 进程

  

ngx_process_cycle.c

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进程执行方法体:

  

ngx_process_cycle.c

//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红黑树,并执行相应过期节点的回调函数

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 


ngx_process_cycle.c

static void

ngx_cache_manager_process_handler(ngx_event_t *ev)

{

   …

   …

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

        if (path[i]->manager) {

            n = path[i]->manager(path[i]->data);//实际上就是ngx_http_file_cache_manager,在ngx_http_file_cache_set_slot函数中完成的manager的注册

            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

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

--------------------------------------------------- --------华丽的函数分割线------------------------------------------------------------------

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;

 

 

 

 

 

 

 

 

 

 

 

 

 

 

宗上所述,过期队列中的删除过期节点的过程如下:

6.流程描述

综上所述: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中完成对缓存过期队列的遍历,并删除过期缓存占用的内存(红黑节点、过期队列节点等)。