`
bollaxu
  • 浏览: 219480 次
  • 性别: 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缓存的方法

    - keys_zone=proxycache:60m:定义一块共享内存区域,名称为proxycache,大小为60MB,用于存放缓存的key和元数据。 - max_size=120m:设置缓存的最大空间大小,超过这个大小后,会根据LRU(最近最少使用)算法删除最...

    nginx_cache_purge.zip

    《Nginx Cache Purge:高效管理Web缓存的利器》 在当今互联网环境中,Web服务器的性能优化至关重要,而缓存技术则是其中的关键一环。Nginx,以其高性能、稳定性以及模块化的特性,成为了许多网站首选的反向代理和...

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

    由于其高性能和稳定性,Nginx在互联网上广泛用于提供静态内容服务。...不过在运行脚本之前,了解nginxproxy_cache的工作机制和脚本的执行逻辑是很有必要的,以确保可以正确处理任何可能出现的问题。

    nginx静态文件缓存的解决方案1

    proxy_cache_path /usr/local/nginx/cache levels=1:2 keys_zone=cache_one:200m inactive=1d max_size=30g; # ... } ``` 这里,`proxy_cache_path` 指定了缓存存储的路径,`levels` 参数定义了缓存目录的层级,`...

    nginx系列(十)nginx缓存代理proxy_cacahe和CDN实现的原理

    “nginx系列(十)nginx缓存代理proxy_cache和CDN实现的原理”这个标题揭示了本文将探讨的主题,主要集中在两个关键点上:一是Nginx的缓存代理机制proxy_cache,二是如何利用Nginx实现内容分发网络(CDN)的功能。...

    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> [.....

    squid、varnish、ngx_cache的性能测试对比报告

    尽管proxy_cache作为Nginx的一个模块,在处理磁盘IO方面可能不如专业的缓存软件,但Nginx的高并发处理能力仍为其带来了性能优势。 Squid在此次测试中的综合表现最佳,尤其在磁盘IO处理方面表现突出。这一点可能是...

    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 cache 学习总结 1

    【Nginx Cache 学习总结】 在Nginx中,缓存机制是一个重要的功能,它可以帮助提升网站的响应速度,减少对后端服务器的压力。本文主要总结了Nginx缓存的一些关键指令和概念。 1. **proxy_cache**: - `proxy_cache...

    Nginx知识体系入门实践.zip

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

    Nginx 性能优化实践1

    3. 缓存更新:Nginx提供了多种机制来控制缓存的更新,如`proxy_cache_revalidate`(根据Last-Modified或ETag头验证缓存)、`proxy_cache_bypass`(决定是否跳过缓存)和`proxy_cache_lock`(防止同一资源的并发请求...

    nginx的各项详细配置-超多注释

    proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m inactive=60m; server { location /static/ { proxy_cache my_cache; proxy_cache_bypass $http_pragma; proxy_cache_revalidate on;...

    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-...

Global site tag (gtag.js) - Google Analytics