nginx对静态文件cache的处理分析
2014-06-29 11:53
295 查看
原文链接http://www.pagefault.info/?p=123 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设置为ngx_open_file_cleanup 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); }
这里ngx_cached_open_file_s有3个域需要注意,分别是uniq,count以及close。其中uniq也就是文件属性中的 st_ino(同一个设备中的每个文件,这个值都是不同的),这个值主要用于判断文件是否被修改(不过这个修改是覆盖这类的,如果你用open打开,然后写入的话,这个值还是一样的);count是文件的引用计数,表示现在文件被几个请求使用中;close表示文件是否需要被关闭。 接下来就是nginx的超时处理策略了。首先来看ngx_close_cached_file,这个函数主要是尝试关闭当前的文件,对这代码我们来看流程。
//判断是否需要关闭 if (!file->close) { //如果不需要,则更新存取时间 file->accessed = ngx_time(); //然后将文件插入到队列头(先remove然后insert) ngx_queue_remove(&file->queue); ngx_queue_insert_head(&cache->expire_queue, &file->queue); //看文件的使用次数是否大于设置的最小次数,或者文件的引用技术是否大于0,
//如果有一个满足,则直接返回,因为此时不需要close文件. if (file->uses >= min_uses || file->count) { return; } } ngx_open_file_del_event(file); if (file->count) { return; } //到达这里说明文件需要被关闭。 if (file->fd != NGX_INVALID_FILE) { //关闭文件 if (ngx_close_file(file->fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, log, ngx_errno, ngx_close_file_n " \"%s\" failed", file->name); } file->fd = NGX_INVALID_FILE; } if (!file->close) { return;
}接下来是ngx_expire_old_cached_files,这个函数比较关键,主要是用来执行超时,或者强制超时cache中的元素(根据参数 不同)。主要的判断就是now – file->accessed <= cache->inactive,这段主要是看这个文件多久没有被使用,是否超过了我们设置的inactive时间。
while (n < 3) {//如果队列为空,则直接返回if (ngx_queue_empty(&cache->expire_queue)) {return;}//取出最后一个文件,也就是可能需要被超时的文件(因为尾部是最长时间没有操作的文件)q = ngx_queue_last(&cache->expire_queue);file = ngx_queue_data(q, ngx_cached_open_file_t, queue);//n是控制是强制超时,还是按inactive超时,后一个判断是判断是否超时if (n++ != 0 && now - file->accessed <= cache->inactive) {return;}//如果有超时的,或者需要强制超时,则开始从队列和红黑树中移除ngx_queue_remove(q);ngx_rbtree_delete(&cache->rbtree, &file->node);cache->current--;ngx_log_debug1(NGX_LOG_DEBUG_CORE, log, 0,"expire cached open file: %s", file->name);if (!file->err && !file->is_dir) {//关闭文件file->close = 1;ngx_close_cached_file(cache, file, 0, log);} else {ngx_free(file->name);ngx_free(file);}}最后来看,nginx里面如何判断文件被改变的。当需要打开一个文件,然后在cache中发现了文件,此时就需要判断文件是否被改变,这里nginx只 能判断删除,然后覆盖的这类,而你如果只是简单的附加,则nginx无法知晓。来看代码,在ngx_open_and_stat_file里面的。判断很简单,就是看uniq是否相等,如果不等则说明文件被改变,此时需要重新打开文件,然后重新加入cache,否则直接返回。
if (of->fd != NGX_INVALID_FILE) {if (ngx_file_info(name, &fi) == NGX_FILE_ERROR) {of->failed = ngx_file_info_n;goto failed;}//判断文件是否被改变if (of->uniq == ngx_file_uniq(&fi)) {goto done;}}//否则重新打开if (!of->log) {/** Use non-blocking open() not to hang on FIFO files, etc.* This flag has no effect on a regular files.*/fd = ngx_open_file(name, NGX_FILE_RDONLY|NGX_FILE_NONBLOCK,NGX_FILE_OPEN, 0);} else {fd = ngx_open_file(name, NGX_FILE_APPEND, NGX_FILE_CREATE_OR_OPEN,NGX_FILE_DEFAULT_ACCESS);}(全文完)
相关文章推荐
- nginx对静态文件cache的处理分析
- Nginx服务器中静态文件cache的处理流程
- nginx配置静态文件expires时间 cache-control
- nginx 集群配置方式 静态文件处理
- nginx配置2个tomcat的负载均衡+配置静态文件处理(图片等)
- nginx 处理 django 静态文件
- Tomcat处理静态文件DefaultServlet分析
- Nginx将utf8编码的url解码成\x的16进制格式导致无法匹配静态文件的问题处理
- nginx配置静态文件expires时间与cache-control
- 让Nginx处理Django的静态文件
- Nginx静态文件处理
- SpringMVC处理静态文件源码分析
- SpringMVC处理静态文件源码分析
- 让Nginx处理Django的静态文件
- SpringMVC处理静态文件源码分析
- 超详细的django1.8处理centos下nginx上处理静态文件步骤!
- Tomcat处理静态文件DefaultServlet分析(转载)
- SpringMVC处理静态文件源码分析
- nginx静态文件处理
- 超详细的django1.8处理centos下nginx上处理静态文件步骤!