`
bollaxu
  • 浏览: 216985 次
  • 性别: Icon_minigender_1
  • 来自: 上海
社区版块
存档分类
最新评论

Nginx Proxy Cache分析

阅读更多

本文从几个部分来详细介绍Nginx的proxy cache功能。第一部分,主要介绍proxy cache的过期、空间管理等。第二部分,主要介绍在Nginx(作为反向代理服务器)收到请求之后,如何检查本地的缓存来确定是否要向后端服务器发起请求。第三部分,主要介绍Nginx向后端服务器发起请求并收到回复的情况下,如何把响应回复缓存到本地。



第一部分


在Nginx中,如果启用了proxy cache功能,master process会在启动的时候启动管理缓存的两个子进程(区别于处理请求的子进程)来管理内存和磁盘的缓存个体。第一个进程的功能是定期检查缓存,并将过期的缓存删除;第二个进程的作用是在启动的时候将磁盘中已经缓存的个体映射到内存中(目前Nginx设定为启动以后60秒),然后退出。


具体的,在这两个进程的ngx_process_events_and_timers()函数中,会调用ngx_event_expire_timers()。Nginx的ngx_event_timer_rbtree(红黑树)里面按照执行的时间的先后存放着一系列的事件。每次取执行时间最早的事件,如果当前时间已经到了应该执行该事件,就会调用事件的handler。两个进程的handler分别是ngx_cache_manager_process_handler和ngx_cache_loader_process_handler。因为ngx_process_events_and_timers()是被循环调用的,所以上面两个handler也会被调用多次。


1. ngx_cache_manager_process_handler

这个函数调用了每个磁盘缓存路径对应的manager()函数,即ngx_http_file_cache_manager()函数。这个函数很简单,就是检查缓存队列,看里面的索引信息有没有过期,如果过期,就把缓存的文件从磁盘删掉,并把索引从内存中释放。

static time_t ngx_http_file_cache_manager(void *data)
{

	ngx_http_file_cache_t  *cache = data;
	//先删过期的缓存
	next = ngx_http_file_cache_expire(cache);

	 for ( ;; ) {
		//获取最更新的缓存空间的大小
		ngx_shmtx_lock(&cache->shpool->mutex);
		size = cache->sh->size;
		ngx_shmtx_unlock(&cache->shpool->mutex);

		//如果空间在指定范围内,不用再删了。return
		//...

		//如果size超过磁盘的使用空间,即size >= cache->max_size
		//强制把部分缓存删除,以保证缓存使用的空间在指定范围内
		next = ngx_http_file_cache_forced_expire(cache);

		//休息一下以后继续删
		//...
	}
}
 

2. ngx_cache_loader_process_handler

这个函数跟ngx_cache_manager_process_handler差不多,不过在里面调用了对应路径的loader()函数,即ngx_http_file_cache_loader()。这个函数的作用是给磁盘缓存路径下面子路径下面的所有缓存文件建立内存索引,并根据文件名得到key来插入红黑树,并放入过期删除队列。

static void ngx_http_file_cache_loader(void *data)
{
	ngx_http_file_cache_t  *cache = data;
	ngx_tree_ctx_t  tree;

	//进入loading状态
	//...

	//初始化tree
	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;

	//ngx_walk_tree是递归函数,打开每层路径(dir)直到每个文件(file),根据其路径和文件名得到key,在缓存的rbtree(红黑树)里面找这个key(部分),如果没有找到的话,就在内存中分配一个映射这个文件的node(但是不会把文件的内容进行缓存),然后插入到红黑树中和加入队列。
	//ctx->file_handler=>
	//ngx_http_file_cache_manage_file=>
	//ngx_http_file_cache_add_file=>
	//ngx_http_file_cache_add
	//从n = ngx_read_file(...)函数可以看出,每个磁盘缓存文件的开头的sizeof(ngx_http_file_cache_header_t)个byte存放了跟缓存相关的信息
	ngx_walk_tree(&tree, &cache->path->name);

	//...
}
  

第二部分


在http的请求发送给有proxy_cache和proxy_pass定义的location的时候,会调用到ngx_http_proxy_handler()。其中在决定是否要向后端发送请求的时候,先检查一下本地是否有缓存一个copy。具体的位置是在ngx_http_upstream_init_request()函数的一开始,在发起向后的连接之前。

static void ngx_http_upstream_init_request(ngx_http_request_t *r)
{
	//在"proxy_cache"的set()即ngx_http_proxy_cache()中设置
	////就是plcf->upstream.cache
	//这儿cache是ngx_shm_zone_t类型的
	if (u->conf->cache) {
		//寻找缓存
		//如果返回NGX_DECLINED就是说缓存中没有,请向后端发送请求
		rc = ngx_http_upstream_cache(r, u);
		if (rc == NGX_BUSY) {
			//重新试一次
			r->write_event_handler = ngx_http_upstream_init_request;
			return;
		}
		r->write_event_handler = ngx_http_request_empty_handler;
		if (rc == NGX_DONE) {
			//在缓存中找到了
			return;
		}

		if (rc != NGX_DECLINED) {
			ngx_http_finalize_request(r, rc);
			return;
		}
	}

	//向后端发起请求
	//...
}

 

 

第三部分


在Nginx收到后端服务器的响应之后,会把这个响应发回给用户。而如果缓存功能启用的话,Nginx就会把响应存入磁盘里。

 

static void ngx_http_upstream_send_response(ngx_http_request_t *r, ngx_http_upstream_t *u)
{
	rc = ngx_http_send_header(r);

	//...

	#if (NGX_HTTP_CACHE)
	//是否要缓存,即proxy_no_cache指令
	//...

	//如果要缓存,设置有效时间等
	//...
	#endif

	//...

	//ngx_http_upstream_process_upstream ==> 1. ngx_event_pipe ==>
	//ngx_event_pipe_read_upstream ==> ngx_event_pipe_write_chain_to_temp_file ==>
	//ngx_write_chain_to_temp_file ==> ngx_create_temp_file --> ngx_write_chain_to_file
	//==>2. ngx_http_upstream_process_request ==> ngx_http_file_cache_update
	//在这儿把从后端获取的文件写到磁盘
	ngx_http_upstream_process_upstream(r, u);
}
 

 

附:proxy cache的配置、set()函数和初始化函数

在Nginx配置文件里面设置proxy_cache_path指令,指定缓存内存的大小和在disk文件系统的路径。如:

#...

http {

	#...

	proxy_cache_path /var/proxy_cache_dir levels=1:2 keys_zone=cache_one:500m max_size=30g;

	server {
		#...
		location / {
			proxy_cache cache_one;
			#...
		}
		#...
	}

	#...
}
 

在解析配置文件的时候,"proxy_cache_path"指令的set()函数是ngx_http_file_cache_set_slot()。

 

char * ngx_http_file_cache_set_slot(...)
{
	//分配内存给cache结构
	ngx_http_file_cache_t  *cache;
	cache = ngx_pcalloc(cf->pool, sizeof(ngx_http_file_cache_t));
	cache->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));

	//获取缓存在disk文件系统的路径
	value = cf->args->elts;
	cache->path->name = value[1];//第一个arg

	//去掉path最后的"/"
	//如果path不是以"/"开头,即是一个相对路径,就在前面加上$nginx_home的路径
	//...

	//解析其他的参数
	for(;;){
		//"level"
		//设置cache->path->level,cache->path->len

		//"keys_zone"
		//设置name.data和name.len,即缓存的名称

		//"inactive"
		//设置缓存驻留时间

		//"max_size"
		//设置硬盘大小
	}

	cache->path->manager = ngx_http_file_cache_manager;
	cache->path->loader = ngx_http_file_cache_loader;
	cache->path->data = cache;

	//检查cache->path是否已经存在
	//如果不存在,则添加到cf->cycle->pathes
	ngx_add_path(cf, &cache->path);

	//name是缓存区名称,size是内存缓存大小
	//遍历cf->cycle->shared_memory的part链表寻找是否已经存在同名的内存空间。如果不存在,就分配一个大小为ngx_shm_zone_t的内存单元,放在part链表最后一个单元的elts。
	//shared_memory为ngx_list_t结构,part为ngx_list_part_s结构
	cache->shm_zone = ngx_shared_memory_add(cf, &name, size, cmd->post);

	//注册回调函数等
	cache->shm_zone->init = ngx_http_file_cache_init;
	cache->shm_zone->data = cache;
	cache->inactive = inactive;
	cache->max_size = max_size;
}
 

 

 在cycle初始化的时候,会初始化内存索引的空间

 

ngx_cycle_t * ngx_init_cycle(...)
{
	//...

	part = &cycle->shared_memory.part;
	shm_zone = part->elts;

	//遍历链表中每个ngx_list_part_t的每个单元
	for(;;){

		//检查当前ngx_list_part_t是否已经遍历完成。若是,则跳到下个ngx_list_part_t
		//...

		//检查每个单元,即ngx_shm_zone_t的shm的size是否为0
		//检查shm的init函数是否有注册,如果没有就说明这个shm没有被用到
		//...

		shm_zone[i].shm.log = cycle->log;
		//old_cycle的shared_memory
		opart = &old_cycle->shared_memory.part;
		oshm_zone = opart->elts;

		//遍历old_cycle的shared_memory每个ngx_list_part_t的每个单元
		for(;;){
			//检查当前ngx_list_part_t的所有单元是否已经遍历完成。若是,则跳到下个ngx_list_part_t
			//...

			//检查是否同名,不同名则跳到下一个单元
			//...

			//找到同名且shm的size和addr匹配
			//调用init函数,并跳到下一个new cycle的shm_zone
			shm_zone[i].init(&shm_zone[i], oshm_zone[n].data);

			//同名但size和addr不匹配,说明old_cycle的oshm_zone无效,把它释放
			ngx_shm_free(&oshm_zone[n].shm);
			break;
		}

		//在old_cycle没有找到跟新cycle的shm_zone[i]匹配的shm
		//分配共享内存给&shm_zone[i].shm,用mmap或者shmget/shmat
		ngx_shm_alloc(&shm_zone[i].shm);

		//把 &shm_zone[i].shm初始化一个ngx_slab_pool_t结构的变量sp
		//对sp成员赋值,调用ngx_shmtx_create()给sp->mutex初始化锁
		//ngx_slab_init(),对分配的共享内存进行初始化
		ngx_init_zone_pool(cycle, &shm_zone[i]);
		//ngx_http_file_cache_init()
		shm_zone[i].init(&shm_zone[i], NULL);
	}
}

 

 

ngx_http_file_cache_init(ngx_shm_zone_t *shm_zone, void *data)
{
	ngx_http_file_cache_t  *ocache = data;
	cache = shm_zone->data;
	//如果ocache不是NULL,即有old cache,就比较缓存路径和level等,如果match的话就继承ocache的sh、shpool、bsize等
	//...

	//如果没有old cache
	cache->shpool = (ngx_slab_pool_t *) shm_zone->shm.addr;
	//ngx_slab_alloc_locked()
	cache->sh = ngx_slab_alloc(cache->shpool, sizeof(ngx_http_file_cache_sh_t));

	cache->shpool->data = cache->sh;
	//初始化红黑树
	//把rbtree的insert函数设为ngx_http_file_cache_rbtree_insert_value()
	ngx_rbtree_init(&cache->sh->rbtree, &cache->sh->sentinel, ngx_http_file_cache_rbtree_insert_value);
	//初始化一个queue
	ngx_queue_init(&cache->sh->queue);
	cache->sh->cold = 1;
	cache->sh->loading = 0;
	cache->sh->size = 0;
	//获取文件系统的block size
	cache->bsize = ngx_fs_bsize(cache->path->name.data);
	//max_size成了总共的block数量
	cache->max_size /= cache->bsize;
	len = sizeof(" in cache keys zone \"\"") + shm_zone->shm.name.len;
	cache->shpool->log_ctx = ngx_slab_alloc(cache->shpool, len);
}

  

索引缓存文件ngx_http_file_cache_lookup


 文件缓存格式

 

 

缓存过期队列

 

  • 大小: 125.1 KB
  • 大小: 57.1 KB
  • 大小: 120.8 KB
1
0
分享到:
评论

相关推荐

    Nginx启用proxy_cache缓存的方法

    本篇文章主要介绍了Nginx启用proxy_cache缓存的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

    nginx proxy_cache批量清除缓存的脚本介绍

    今天小编就为大家分享一篇关于nginx proxy_cache批量清除缓存的脚本介绍,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

    Nginx反向代理proxy_cache_path directive is not allowed错误解决方法

    nginx: [emerg] “proxy_cache_path” directive is not allowed here in /etc/nginx/conf.d/default.conf:29 提示意思“proxy_cache_path指令不被允许”,在官网上查找了相关说明,也没有发现问题,最后看应用范围...

    nginx缓存清除插件ngx_cache_purge.zip

    ngx_cache_purge 是 nginx 模块,此模块可以清理 nginx 的 FastCGI、proxy、 SCGI 和 uWSGI 的缓存。配置指令(相同位置语法)fastcgi_cache_purgesyntax: fastcgi_cache_purge on|off|<method> [from all|<ip> [.....

    Nginx知识体系入门实践.zip

    06.Nginx提供ProxyCache缓存服务 07.Nginx Rewrite跳转规则与实践 08.Nginx构建Https加密传输网站(基于IOS苹果要求) 09.Nginx构建动态网站架构lnmp&lnmt 10Nginx+Lua-实战代码灰度发布实战-WAF防火墙 11.Nginx性能...

    使用Nginx反向代理与proxy_cache缓存搭建CDN服务器的配置方法

    碰到问题:移动用户访问web服务器www.osyunwei.com很慢解决办法:1、在移动机房放置一台nginx反向代理服务器2、通过域名DNS智能解析,所有移动用户访问www.osyunwei.com时解析到nginx反向代理服务器3、nginx反向代理...

    史上最牛逼的Nginx最佳实践教程从入门到精通

    19.Nginx+keepalived+proxy_cache配置高可用Nginx群集和高速缓冲 20.Nginx最难一战 优化指南 21.确保Nginx安全的10大关键技巧 Nginx变量详解 Nginx模块与实践案例 提供了目前所有互联网公司都在用的51个案例 常见...

    nginx cache 学习总结 1

    This directive appeared in version 1.1.12.当指令被指定时,根据 proxy_cache_key指令确定的若个或得出相同

    Nginx缓存Cache的配置方案以及相关内存占用问题解决

    nginx缓存cache的5种方案  1、传统缓存之一(404)  这个办法是把nginx的404错误定向到后端,然后用proxy_store把后端返回的页面保存。  配置:  location / {  root /home/html/;#主目录  expires 1d;#网页...

    实战nginx-张宴

    9.3 新浪网开源软件项目——基于Nginx的NCACHE网页缓存系统 第3部分 实战篇 第10章 Nginx在国内知名网站中的应用案例 10.1 Nginx反向代理与负载均衡类网站应用案例 10.2 Nginx+PHP类网站应用案例 10.3 Nginx视频...

    Nginx RPM 包定制制作

    /application/nginx-1.6.3/proxy_temp /application/nginx-1.6.3/sbin/nginx /application/nginx-1.6.3/scgi_temp /application/nginx-1.6.3/uwsgi_temp [root@nginx tools]# sz nginx-1.6.3-1.x86_64.rpm

    深入Nginx + PHP 缓存详解

    Nginx缓存nginx有两种缓存机制:fastcgi_cache和proxy_cache下面我们来说说这两种缓存机制的区别吧proxy_cache作用是缓存后端服务器的内容,可能是任何内容,包括静态的和动态的fastcgi_cache作用是缓存fastcgi生成的...

    nginx cache不缓存问题的原因与解决方案

    proxy_cache_path /nginx/cache/path levels=1:2 keys_zone=cache_test:2048m inactive=7d max_size=10g; ...... location ~ .(gif|jpg|jgep|png)$ { proxy_pass http://upstreams; proxy_ignore_headers X-...

    实战Nginx.取代Apache的高性能Web服务器

    9.3 新浪网开源软件项目——基于Nginx的NCache网页缓存系统 第3部分 实战篇 第10章 Nginx在国内知名网站中的应用案例 10.1 Nginx反向代理与负载均衡类网站应用案例 10.2 Nginx+PHP类网站应用案例 第11章 Nginx...

    详解用Nginx搭建CDN服务器方法(图文)

    利用Nginx的proxy_cache搭建缓存服务器一:编译ngx_cache_purge 1、Nginx的Proxy_cache是根据Key值md5哈希存储缓存,支持任意的Key,例如你可以根据”域名、URI、参数”组合成key,也支持非200状态码,如404/302等...

    实战Nginx高性能Web服务器

    13、高性能Web服务器Nginx的配置与部署研究(13)应用模块之Memcached模块+Proxy_Cache双层缓存模式 内容:讲述一种提供双层缓存抗穿透的HTTP服务缓存解决方案。 14、高性能Web服务器Nginx的配置与部署研究(14)...

    实战Nginx:取代Apache的高性能Web服务器 第一章

    9.3 新浪网开源软件项目——基于Nginx的NCache网页缓存系统 第3部分 实战篇 第10章 Nginx在国内知名网站中的应用案例 10.1 Nginx反向代理与负载均衡类网站应用案例 10.2 Nginx+PHP类网站应用案例 第11章 Nginx的非...

Global site tag (gtag.js) - Google Analytics