Nginx教程:nginx对静态文件cache的处理

  Nginx中对静态文件进行了Cache,对应的命令就是open_file_cache,open_file_cache_min_uses以及open_file_cache_valid。这次我就来分析下nginx如何对静态文件进行cache的。

  要注意一个就是open_file_cache的 inactive表示文件多久不被访问就会从cache中删除.

  首先来描述一下Linux下是如何做的,因为这里nginx对于bsd版本有一个不同的做法,这是因为bsd中可以给kqueue监听文件改变的事件。而linux下,nginx并没有使用Inotify,而是每次都会判断文件的st_ino来得到文件是否被修改,不过这样会有个缺点就是如果你是使用open,然后write来修改文件的话,文件其实是相同的,因此st_ino是相同的,此时nginx是无法知道的,因此修改的话,最好使用会先删除再覆盖的命令(比如cp)。

  首先,nginx的cache只是cache句柄,因为静态文件的发送,一般来说,nginx都是尽量使用sendfile进行发送的,因此之需要cache句柄就够了。

  所有的cache对象包含在两个数据结构里面,整个机制最关键的也是这两个东西,一个是红黑树,一个是一个队列,其中红黑树是为了方便查找(需要根据文件名迅速得到fd),而队列为了方便超时管理(按照读取顺序插入,在头的就是最近存取的文件),由于所有的句柄的超时时间都是一样的,因此每次只需要判断最后几个元素就够了,因为这个超时不需要那么实时.

  假设现在客户端请求是GET test.html HTTP/1.1 ,则nginx是这么处理的,如果test.html在cache中存在,则从cache中取得这个句柄,然后正常返回,如果test.html不存在,则是打开这个文件,然后插入到cache中。不过这里有很多细节都需要处理,比如超时,比如红黑树的插入等等,接下来,我们就对照着代码来看这些都是如何处理的。

  主要代码都是包含在ngx_open_cached_file中的。

  首先,这里,cache的红黑树的key是一个hash值,是文件名的crc校验码:

//计算hash
hash = ngx_crc32_long(name->data, name->len);
//根据名字查找对应的file对象。
file = ngx_open_file_lookup(cache, name, hash);

  首先我们来看如果没有找到cache对象的情况,此时nginx会open 这个文件,然后保存对应的信息到cache中。

//打开文件,保存文件信息
rc = ngx_open_and_stat_file(name->data, of, pool->log);

if (rc != NGX_OK && (of->err == 0 || !of->errors)) {
goto failed;
}

create:
//max为open_file_cache命令中定义的那个max指令,而current也就是当前cache的文件个数
if (cache->current >= cache->max) {
//如果大于max,则需要强制expire几个元素
ngx_expire_old_cached_files(cache, 0, pool->log);
}
...........................................................

ngx_cpystrn(file->name, name->data, name->len + 1);

file->node.key = hash;

//插入到红黑树中。

ngx_rbtree_insert(&cache->rbtree, &file->node);

//更新current

cache->current++;

  然后就是更新队列的部分,这里可以看到插入是每次都插入到队列的头,之所以插入到头,是为了超时操作更方便。还有一个需要注意的地方就是绑定ngx_pool_cleanup_t的handler,我们知道nginx中可以给pool绑定对应的handler,绑定后,当pool被释放的时候,就会调用这个handler,这里nginx给request pool绑定了一个handler,这里是为了处理超时的,后面我们会分析到。

renew:
//更新创建时间
file->created = now;
found:
//更新存取时间
file->accessed = now;
//将文件插入到超时队列中。
ngx_queue_insert_head(&cache->expire_queue, &file->queue);
if (of->err == 0) {
if (!of->is_dir) {
//这里很关键,将cln的handler
cln->handler = ngx_open_file_cleanup;
ofcln = cln->data;
ofcln->cache = cache;
ofcln->file = file;
ofcln->min_uses = of->min_uses;
ofcln->log = pool->log;
}
return NGX_OK;
}
return NGX_ERROR;

  然后就是超时机制是如何实现的。在nginx中并没有通过定时器什么的来实现,而是通过nginx的一个特性,那就是每个request结束的时候,都会清理掉他所分配的pool,而nginx就是给每个有打开文件的request都绑定了对应clean handler,当request pool被释放的时候,就会来根据时间来判断是否已经超时,这里的clean handler就是ngx_open_file_cleanup。

static void
ngx_open_file_cleanup(void *data)
{
ngx_open_file_cache_cleanup_t *c = data;
c->file->count--;
//关闭
ngx_close_cached_file(c->cache, c->file, c->min_uses, c->log);

/* drop one or two expired open files */

//处理超时
ngx_expire_old_cached_files(c->cache, 1, c->log);
}

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/10934.html