nginx中各个级别缓存的原理

本文详细介绍了nginx的缓存机制,包括304响应、静态级别缓存和动态级别缓存的工作原理。通过缓存,nginx提升了处理速度,减少了对后端资源的压力。静态级别缓存保存文件句柄,动态级别缓存则存储动态请求的响应内容。通过对请求的处理,nginx实现了高效的缓存管理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

nginx一向以高性能著称,缓存是其中一大利器,本文将分享nginx中各个级别缓存的原理和内部是如何实现的。
  
  0.什么是缓存
  
  缓存存在计算机中的各个领域,其目的都是为了提升处理速度,可以说是以空间换取了时间。但事实并不是这么简单,因为引入了缓存,就要处理一致性、过期处理、缓存策略和命中率等。例如CPU有几级缓存,memcached作为内存缓存服务器,redis也是,还有文件缓存服务器,CDN等。总而言之,访问目标后‘备份’到某中间层,下次再访问时,直接从中间层获取,加快了访问速度。所以目标越往后,处理速度越慢,程序要做的就是尽量命中在前面,不要让访问穿透到后面目标。
  
  1.nginx缓存
  
  nginx属于web应用服务器,是典型的request/response模式。整体而言,nginx的缓存可以总结为3个:304响应、静态级别缓存、动态级别缓存。下面一一介绍:
  
  *304响应
  
  这个其实不算真正意义上的缓存,之所以提出来,是因为它的重要性,也因为涉及客户端的缓存。当客户端访问某URL时,如果服务器响应304状态码,其意义是告诉客户端您访问的东西没有变化过,内容从自己本地获取。浏览器就是典型的应用。当你第一次访问某静态资源时,响应200,当按F5时,会响应304。实现原理是nginx根据头部信息if_modified_since与静态资源的时间戳比较,如果一样,则返回304。做web开发应该清楚这个状态码。有变化过,内容从自己本地获取。浏览器就是典型的应用。当你第一次访问某静态资源时,响应200,当按F5时,会响应304。实现原理是nginx根据头部信息if_modified_since与静态资源的时间戳比较,如果一样,则返回304。做web开发应该清楚这个状态码。
  
*静态级别缓存

    location/test{
    open_file_cachemax=100;
    }

这个级别的缓存是缓存静态资源文件的句柄。nginx处理一个请求时,会打开一个文件句柄,然后在请求处理结束后关闭它。加入open_file_cache后,就会将文件句柄保存在工作进程中(不是共享内存),只要工作进程存在,当后面的请求访问同样的文件时,就不用重新打开文件。这样的好处是节省了文件打开关闭的开销。

*动态级别缓存

    http{
    ...
    fastcgi_cache_path/tmp/abckeys_zone=c1:10M;
    server{
    location~\.php${
    ...
    fastcgi_cachec1;
    fastcgi_cache_valid20030s;
    }
    }
    }

这个级别的缓存是缓存动态请求的内容。当客户端访问某动态文件时(http://example.com/test.php),nginx将请求重新封装成fastcgi协议,然后转发给php-fpm处理,当php-fpm正常响应后,nginx将响应内容保存到某目录的文件里。下次同样的请求过来,就直接从缓存目录的文件中读取,比第一次快且省事很多。
  
  聪明的你可能已经想到动态级别缓存可以再加上静态级别缓存,没错的。这3种响应是独立的,可以灵活配合使用。现在您已经明白缓存的原理,接下来看下如何实现的
  
  2.304响应
  
  ngx_http_not_modified_filter_module处理了这个功能,属于httprequestheaderfilter模块。源码非常简单。

    staticngx_int_tngx_http_not_modified_header_filter(ngx_http_request_t*r) {
    ...    if(r->headers_in.if_modified_since){
             if(r->headers_in.if_modified_since&&ngx_http_test_if_modified(r)) {
                  returnngx_http_next_header_filter(r);
                  }
    /*notmodified*/
               r->headers_out.status=NGX_HTTP_NOT_MODIFIED;
               returnngx_http_next_header_filter(r);
               }
               returnngx_http_next_header_filter(r);
             }
     staticngx_uint_tngx_http_test_if_modified(ngx_http_request_t*r){
    ...
            if(r->headers_out.last_modified_time==(time_t)-1){
                          return1;
               }
         clcf=ngx_http_get_module_loc_conf(r,ngx_http_core_module);
            if(clcf->if_modified_since==NGX_HTTP_IMS_OFF){
            ims=ngx_parse_http_time(r->headers_in.if_modified_since->value.data,
            r->headers_in.if_modified_since->value.len);
            if(ims==r->headers_out.last_modified_time){
                return0;
               }
            if(clcf->if_modified_since==NGX_HTTP_IMS_EXACT||ims<r->headers_out.last_modified_time){
                  return1;
               }
                  return0;
              }

3.静态级别缓存

 typedefstruct{
 ngx_rbtree_trbtree;
 ngx_rbtree_node_tsentinel;
 ngx_queue_texpire_queue;
 ngx_uint_tcurrent;
 ngx_uint_tmax;
 time_tinactive;
 }
 ngx_open_file_cache_t;

记得这个只在工作进程中处理,不涉及共享内存。看的出来ngx_open_file_cache_t是一个红黑树,也是一个队列。每个文件的文件(根据完整文件名)对应一个节点。这样就完整的维护了所有的可能要缓存的文件。又因为缓存需要处理过期,所以最近访问的文件会保持在expire_queue队列的前面,每次访问将从队列中删除,然后移到最前面。整个逻辑封装在一个函数里:

ngx_int_tngx_open_cached_file(ngx_open_file_cache_t*cache,ngx_str_t*name,
ngx_open_file_info_t*of,ngx_pool_t*pool){...}

4.动态级别缓存

    ngx_http_file_cache_t{
    ngx_http_file_cache_sh_t{
    tree
    queue
    ...
    }
    }
    ngx_http_cache_t{
    keys
    key
    ngx_http_file_cache_node_t
    }

如果简化点处理过程就是:根据url算出md5,然后将动态请求响应内容存储到文件名为某md5的文件里。

http://example.com/a.php->d41d8cd98f00b204e9800998ecf8427e->/tmp/abc/d41d8cd98f00b204e9800998ecf8427e

http://example.com/b.php->d41d8cd98f00b204e9800998ecf8432d->/tmp/abc/d41d8cd98f00b204e9800998ecf8432d

http://example.com/c.php->d41d8cd98f00b204e9800998ecf84a32->/tmp/abc/d41d8cd98f00b204e9800998ecf84a32

因为文件存储在硬盘里,所以这里用到共享内存,而不是工作进程的内存中,来保存这种对应关系。
  
我们可以将每个请求(r)当作各个缓存节点,对应ngx_http_cache_t。ngx_http_file_cache_t就是缓存的集合。明白这个关系后,我们解释下整个流程是如何从开始到结束的。

a)根据fastcgi_cache_path的keys_zone,nginx创建了多个ngx_http_file_cache_t。根据fastcgi_cache指定了某个location将用到哪个ngx_http_file_cache_t。
  
b)为每个请求创建一个ngx_http_cache_t(在ngx_http_file_cache_new函数里),然后继续产生一个key,作为本节点的索引。你可以将请求、请求响应内容、内容存储文件都当作同一个东西,最终都是节点,对应ngx_http_cache_t。所以需要有key这个东西。

c)处理缓存目录和文件名

d)nginx处理动态请求是在ngx_event_pipe里的,当需要缓存里,pipe会有个temp_path将响应内容存储到这个文件里

e)最后将pipe的temp_path重命名成前面产生的文件名,结束了。

5.小结

动态请求的缓存处理代码还是有点复杂的,有兴趣的同学研究下其如何实现很有好处,可以体会下nginx的代码精致程度。

推荐阅读:jvm培训:如何判断哪些对象需要回收?

如果你想了解更多关于java架构师的专业知识,可以加入JAVA架构师交流群:1160405674,里面都是同行,有资源分享包括但不限于(分布式架构、高可扩展、高性能、高并 发、Jvm性能调优、Spring,MyBatis,Nginx源码分析,Redis,ActiveMQ、Mycat、Netty、Kafka、Mysql 、Zookeeper、Tomcat、Docker、Dubbo、Nginx)。欢迎一到五年的工程师加入,合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值