NginX进程模型-Cache Manager/Cache Loader
来源:互联网 发布:cordova 源码 编辑:程序博客网 时间:2024/06/03 21:29
NginX多进程模型中,Master进程管理其派生的子进程,Worker进程处理连接请求。在某些情况下,NginX Master进程会启动两个与Cache相关的进程,即:Cache Manager/Cache Loader用于缓存上游服务器的数据(譬如:ngx_http_proxy_module中设置proxy_cache_path)。本文将介绍Cache Manager/Cache Loader的内部运行机制。
NginX中cache的原理是:将上游服务器返回的数据缓存在本地文件中,如果请求命中缓存,则直接通过sendfile将缓存文件的内容发送给下游。以ngx_http_proxy_module为例来说明cache的原理。ngx_http_proxy_module通过proxy_cache_path指令完成file cache的初始化。
1. Cache的管理
Cache的管理由Cache Loader/Cache Manager两个进程合作完成;Cache Manager的功能是定期清理过期缓存(基本策略为LRU);Cache Loader的功能是将已经缓存文件映射到内存中。
1.1 初始化
通过调用ngx_http_file_cache_set_slot函数,NginX能获得一个Cache管理的目录实例;NginX在ngx_init_cycle阶段完成目录创建工作。
ngx_start_cache_manager_processes函数是触发Cache Loader/Cache Manager进程启动的入口。接下来将分别进行介绍。
1.2 Cache Manager
Cache Manager进程的核心逻辑是一个定时器任务处理;其回调函数为ngx_cache_manager_process_handler。接下来分析一下代码:
static voidngx_cache_manager_process_handler(ngx_event_t *ev){ time_t next, n; ngx_uint_t i; ngx_path_t **path; next = 60 * 60; /// 默认为1小时 path = ngx_cycle->paths.elts; for (i = 0; i < ngx_cycle->paths.nelts; i++) { if (path[i]->manager) { n = path[i]->manager(path[i]->data); /// 执行回调(ngx_http_file_cache_manager) 返回n表示下一个过期检查时间点。 next = (n <= next) ? n : next; ngx_time_update(); } } if (next == 0) { next = 1; }<strong> ngx_add_timer(ev, next * 1000); /// 再次将任务追加到定时器中。</strong>}
ngx_http_file_cache_manager回调函数功能为:(1)删除时间过期的缓存文件 (2) 缓存文件大小超限,删除最近最久的缓存文件。接下来看一下代码:
static time_tngx_http_file_cache_manager(void *data){ ngx_http_file_cache_t *cache = data; off_t size; time_t next, wait; 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); 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; } }}
首先看一下ngx_http_file_cache_expire函数,代码分析如下:
static time_tngx_http_file_cache_expire(ngx_http_file_cache_t *cache){ u_char *name, *p; size_t len; time_t now, wait; ngx_path_t *path; ngx_queue_t *q; ngx_http_file_cache_node_t *fcn; u_char key[2 * NGX_HTTP_CACHE_KEY_LEN]; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache expire"); path = cache->path; len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; /// cache key的长度 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_quit || ngx_terminate) { wait = 1; break; } if (ngx_queue_empty(&cache->sh->queue)) { /// 缓存为空,不需要进行删除 wait = 10; break; } q = ngx_queue_last(&cache->sh->queue); fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); /// 取最久未被使用的缓存文件句柄 wait = fcn->expire - now; <strong>if (wait > 0) { /// 没有超时。</strong> 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) { /// 当前引用计数为0, 且已经超时,则执行删除操作。 ngx_http_file_cache_delete(cache, q, name); continue; } if (fcn->deleting) { /// 如果正在删除则返回。 wait = 1; break; } /// 没有超时或者有引用计数大于0,将设置为最新引用。LRU操作。 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); (void) ngx_hex_dump(p, fcn->key, len); /* * abnormally exited workers may leave locked cache entries, * and although it may be safe to remove them completely, * <strong>we prefer to just move them to the top of the inactive queue</strong> */ ngx_queue_remove(q); fcn->expire = ngx_time() + cache->inactive; ngx_queue_insert_head(&cache->sh->queue, &fcn->queue); ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "ignore long locked inactive cache entry %*s, count:%d", 2 * NGX_HTTP_CACHE_KEY_LEN, key, fcn->count); } ngx_shmtx_unlock(&cache->shpool->mutex); ngx_free(name); return wait;}
再来看一下ngx_http_file_cache_forced_expire函数,代码分析如下:
static time_tngx_http_file_cache_forced_expire(ngx_http_file_cache_t *cache){ u_char *name; size_t len; time_t wait; ngx_uint_t tries; ngx_path_t *path; ngx_queue_t *q; ngx_http_file_cache_node_t *fcn; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache forced expire"); path = cache->path; 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); wait = 10; tries = 20; ngx_shmtx_lock(&cache->shpool->mutex); for (q = ngx_queue_last(&cache->sh->queue); q != ngx_queue_sentinel(&cache->sh->queue); q = ngx_queue_prev(q)) /// 从最久往最新遍历 { fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); ngx_log_debug6(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache forced 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) { /// 如果引用计数为0,强制删除,不用管是否过期。然后准备退出。wait时间为0. ngx_http_file_cache_delete(cache, q, name); wait = 0; } else { <strong>/// 最多往前探测20个缓存文件</strong>。 if (--tries) { continue; } wait = 1; /// <strong>探测了20个缓存文件引用计数都非0, wait 1秒钟</strong> } break; } ngx_shmtx_unlock(&cache->shpool->mutex); ngx_free(name); return wait;}
从上述代码可以看出,具体的删除操作的函数为:ngx_http_file_cache_delete。改函数的代码分析如下:
static voidngx_http_file_cache_delete(ngx_http_file_cache_t *cache, ngx_queue_t *q, u_char *name){ u_char *p; size_t len; ngx_path_t *path; ngx_http_file_cache_node_t *fcn; fcn = ngx_queue_data(q, ngx_http_file_cache_node_t, queue); /// 取ngx_http_file_cache_node_t数据。 if (fcn->exists) { /// 如果缓存文件存在 cache->sh->size -= fcn->fs_size; path = cache->path; p = name + path->name.len + 1 + path->len; p = ngx_hex_dump(p, (u_char *) &fcn->node.key, sizeof(ngx_rbtree_key_t)); len = NGX_HTTP_CACHE_KEY_LEN - sizeof(ngx_rbtree_key_t); p = ngx_hex_dump(p, fcn->key, len); *p = '\0'; fcn->count++; fcn->deleting = 1; ngx_shmtx_unlock(&cache->shpool->mutex); len = path->name.len + 1 + path->len + 2 * NGX_HTTP_CACHE_KEY_LEN; ngx_create_hashed_filename(path, name, len); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache expire: \"%s\"", name); if (ngx_delete_file(name) == NGX_FILE_ERROR) { /// 删除文件 ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, ngx_errno, ngx_delete_file_n " \"%s\" failed", name); } ngx_shmtx_lock(&cache->shpool->mutex); fcn->count--; fcn->deleting = 0; } if (fcn->count == 0) { ngx_queue_remove(q); ngx_rbtree_delete(&cache->sh->rbtree, &fcn->node); ngx_slab_free_locked(cache->shpool, fcn); }}
1.3 Cache Loader
Cache Loader进程的核心逻辑也是一个定时器任务处理;其回调函数为ngx_cache_loader_process_handler。接下来分析一下代码:
static voidngx_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->paths.elts; for (i = 0; i < cycle->paths.nelts; i++) { if (ngx_terminate || ngx_quit) { break; } if (path[i]->loader) { path[i]->loader(path[i]->data); /// 实际执行函数为: ngx_http_file_cache_loader函数。 ngx_time_update(); } } exit(0);}
注: 执行一次回调之后,Cache Loader进程退出。启动Cache Loader 60秒之后,执行定时器任务回调函数ngx_cache_loader_process_handler。
接下来,介绍一下ngx_http_file_cache_loader函数:
static voidngx_http_file_cache_loader(void *data){ ngx_http_file_cache_t *cache = data; ngx_tree_ctx_t tree; if (!cache->sh->cold || cache->sh->loading) { /// 系统启动后cold = 1, loading = 0 (loading = 1) 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 cache loader"); 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; tree.data = cache; tree.alloc = 0; tree.log = ngx_cycle->log; cache->last = ngx_current_msec; cache->files = 0; 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);}
接下来分析一下ngx_walk_tree函数。
ngx_int_tngx_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); 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); 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; } } 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) { //// 处理文件的操作。实际执行函数为ngx_http_file_cache_manage_file goto failed; } } 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); 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); 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;}
实际文件的加载函数为ngx_http_file_cache_manage_file,相关代码分析如下:
static ngx_int_tngx_http_file_cache_manage_file(ngx_tree_ctx_t *ctx, ngx_str_t *path){ ngx_msec_t elapsed; ngx_http_file_cache_t *cache; cache = ctx->data; if (ngx_http_file_cache_add_file(ctx, path) != NGX_OK) { /// 将文件添加到Cache (void) ngx_http_file_cache_delete_file(ctx, path); } if (++cache->files >= cache->loader_files) { /// 文件个数太大,则睡眠并清理files ngx_http_file_cache_loader_sleep(cache); } else { ngx_time_update(); elapsed = ngx_abs((ngx_msec_int_t) (ngx_current_msec - cache->last)); ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ngx_cycle->log, 0, "http file cache loader time elapsed: %M", elapsed); if (elapsed >= cache->loader_threshold) { /// loader时间过长 则睡眠 ngx_http_file_cache_loader_sleep(cache); } } return (ngx_quit || ngx_terminate) ? NGX_ABORT : NGX_OK;}
总结: 1> Cache Loader的任务是加载本地的缓存文件,加载完毕后退出。
- NginX进程模型-Cache Manager/Cache Loader
- nginx -cache loader process 进程分析
- nginx -cache loader process进程分析
- 【nginx】-cache loader process进程分析
- Nginx-cache manager process进程分析
- Cache Manager
- nginx cache
- Goldengate Cache Manager(CACHEMGR)
- nginx源码阅读(四).创建子进程(worker和cache)
- nginx的cache功能
- Nginx Proxy Cache分析
- Nginx Proxy Cache分析
- linux 配置Nginx(cache)
- nginx-cache 总结
- nginx cache 配置
- Nginx Proxy Cache分析
- Nginx Cache中$request_filename
- nginx no-cache
- Windows下Mirth连接Sybase数据库
- Android-WebView优化
- Android打印日志工具类
- iOS内存管理浅谈
- 数组参数类型带...
- NginX进程模型-Cache Manager/Cache Loader
- ESB解决方案简要描述
- Eclipse上GIT插件EGIT使用手册
- 如何查看git本地分支对应的远程分支
- QJSON的编译方法
- ORACLE权限管理调研笔记
- RHEL 5.5源码编译go编译器出错解决
- ios 搞轮播广告的超爽控件
- OCX控件打包成CAB