缓存架构系统就谈多级缓存,可我就知Redis!nginx缓存代理助你打开视野

1,776 阅读11分钟

天下武功唯快不破。随着用户访问请求的压力的剧增,服务器可能处理不过增量太快的请求。单用户请求的响应时间将会变长。如果说,有一个什么方案来解决这个问题,那缓存肯定是首选。

那为何唯独以缓存为佳呢?

因为有了缓存,就不用每次走向原始的数据库里面进行查找。直接现场返回。那就是我们常说的Redis吗?

不是,是 Nginx 的代理缓存,可常用的不是Redis、memcache这些吗?

那如果用户请求在走到接入层就拿到了想要的数据,是不是就不用在访问到后面的Redis缓存啦。从而减少了网络I/O上的开销。

那这在那些场景使用呢? 请听我细细道来

Nginx 代理缓存是什么

缓存代理 指 Nginx 把客户端请求代理到真实内容源服务器并缓存响应信息到磁盘或内存,如下次访问相同的 URL 请求,就直接从 Nginx 处获取缓存响应。
因 Nginx 做了响应信息的缓存,而缓存数据是通过代理到真实内容服务器。故称之为 缓存代理

头铁,那什么是代理?代理的方式有哪些呢?

代理就是一个种工作模式,把任务交给别人去完成,你只需等待结果就可以。不用自己全盘接受。

ps:就是第三方的意思

常用代理方式有正、反向代理两大类。

正向代理

客户端发送请求找到代理服务器,由代理服务器向目标服务器发送请求,然后返回响应数据给客户端。整个过程对客户端来说,它是知道目标服务器的真实位置。

比如:你拖朋友从香港代购包包,对于 来说,你是知道 朋友具体要去哪里,做什么事情。

场景: 上网翻墙。一般都是针对外网IP。对用户透明。

国内 吒吒辉无法直接访问 facebook,但能直接访问到像阿里云在国外部署的云服务器,于是我就可以通过云主机做一个代理,帮助吒吒辉无访问到国外的网站。因为吒吒辉直接访问的云主机在国外,不会被国内网络墙掉。

反向代理

客户端向反向代理服务器发送请求后,反向代理服务器将该请求转发给内部网络上的后端服务器,并将从后端服务器上得到的响应结果返回给客户端。

比如:你直接在海淘相关平台上购买包包。对 来说,你不知道海淘公司具体要做些什么

场景:Nnginx+PHP。一般针对内网IP,对用户不透明。

吒吒辉访问 taobao.com场景来看,吒吒辉始终认为访问的是原始服务器,而不是反向代理的服务器。

ps:淘宝使用 Tengine 服务器

但实际上却是反向代理服务器接收了吒吒辉的请求,然后代理到原始服务器取得相关资源后,在返回给吒吒辉

这个是出于安全保护后端资源的措施,只允许代理服务器访问原始资源服务器。 但吒吒辉却不知道。 一般都会后端资源服务器上加上 防火墙,只允许代理服务器来访问

常见的就是:nginx把请求代理到PHP的9000端口,fastcgi_pass 127.0.0.1:9000;

总结:正反向代理是彼此相反,各自互补。

为什么要使用nginx缓存代理

  • 减少后端压力,提高网站请求接入,降低请求延时,有利于节省后端服务器的资源。

小吒吒,这个nginx缓存代理和 浏览器 缓存有什么区别,怎么nginx还整出了这个

如果你提了它,那证明你小伙,心还是细。

熟悉的同学可能都知道面对大量用户请求访问的时候,会把一些资源缓存到本地来处理。比如:图片、视频等资源。

但这种这针对于本地静态的化内容,而缓存代理则是针对服务器端的资源,有些比较敏感或者更新比较多的资源就不太适合放到本地,或者资源不是来源于当前服务器,而是来自于上游的其它服务器站点,用缓存代理的方式就可以提升网络的加载速度。

缓存代理应该如何实现?

缓存代理实现还是得根据资源的动态和静态划分,不同的资源类型实现方式不一样,但一般都针对静态,毕竟动态就在于字,如果后端数据修改了,但是资源却时时未更新上,你说这不是问题?

静态缓存实现

永久缓存配置

配置如下:

server{      
listen  80;    
server_name localhost  
location / {
# 文件保存目录,这里是Windows,如果在Linux需要保证用户执行权限和目录所属用户与nginx启动一致。 
#  注: 文件目录需手动创建
     root D:zhazhahui/cache;
     
     #  开启本地缓存
     proxy_store on; 
     
     #  配置缓存用户和组的读写权限
     proxy_store_access user:rw group:rw all:r;
     
     #  反向代理时接收的数据临时存储文件的目录,nginx启动后会自动创建生成
    proxy_temp_path cache_tmp;
    
## 利用正则表达式匹配缓存目录中的文件、目录或符号链接是否存在
  # !-e 检查一个文件、目录或符号链接是否存在,不存在就执行 ()中的指令
  
  # $request_filename为nginx内置变量,为当前请求的URI,就是你域名站点后面出现的文件路径 
 if(!-e $request_filename) {  
 
       proxy_pass http://zhazhahui.com:8080;  
    }        
}  

临时缓存设置

Nginx的临时缓存,是使用 proxy_cache 指令设置的临时缓存配置,它采用md5算法将请求URL地址进行哈希(hash)计算后,根据配置文件中指定缓存目录,来保存响应后的数据。

那这永久缓存和临时缓存有啥区别?我该如何选择呢? 放心,我都懂,后面细聊,超乎你象限!

  • 缓存配置

nginx.conf 中的 http模块 添加如下 配置。

#代理临时目录
proxy_temp_path /usr/local/nginx/proxy;
#Web缓存目录和参数设置
proxy_cache_path /usr/1ocal/nginx/proxy_cache_dir levels=1:2 keys_zone=cache_one:50m inactive=1m max_size=500m; 
  • /usr/local/nginx/proxy_cache_dir 参数:表示用户自定义的缓存文件保存目录。

    • levels参数:表示缓存目录下的层级目录结构,它是根据哈希后的请求URL地址创建的,目录名称从哈希后的字符串结尾处开始截取。 假设哈希后的请求链接地址为 af7098a15e430326197ee01516fdace0, 则levels=1:2表示,第1层子目录的名称是长度为1的字符0,第2层子目录的名称是长度为2的字符ce。
    • keys_zone参数: 指定缓存区名称及大小,例如:cache_one:50m表示缓存区名称为 cache_one,在内存中的空间是50MB。
    • inactive参数:表示主动清空在指定时间内未被访问的缓存。例如:1m 清空在1分钟内未被访问过的缓存,1h表示1小时,1d表示1天等。
    • max_size参数:表示指定磁盘空间大小。例如,500m、10g。需要注意的是,Nginx在进行缓存时, 首先会被写人 proxy_temp_path 指定的临时目录中,因此建议proxy_cache_path 和proxy_temp_path 指令设置的目录应在同一个文件系统中,避免不同文件系统之间的磁盘I/O消耗。

为啥缓存存储要设置一个临时目录和真实的数据存储呢?

因为NGINX一开始接受请求处理时就会把数据是会写入到临时缓存区里面做暂存,为的是提高上游服务器请求的响应能力,然后等临时目录满了或者时间后台线程时间到了。在批量写入到磁盘的缓存目录里面。从而减少磁盘 I/O开销

  • 在Server块添加临时缓存配置

listen  80;
      server name zhazha.hui.test;
      #增加两个响应头信息,用于获知访问的服务器地址与缓存是否成功
      add_header X-Via $server addr;
      add_header X-Cache $upstream cache status;

      location /{
      #设置缓存区城名称
      proxy_cache cache_one;
      #以域名、URI、参数组合成Web缓存的Key值,Nginx根据Key值哈希
      proxy_cache_key $host$uri$is_args$args;
      
      #对不同的HTTP状态码设置不同的缓存时间
      proxy_cache valid 200 10m;
      #200缓存10分钟
      proxy_cache_valid 304 1m;
      #304缓存1分钟
      proxy_cache_valid 301 302 1h;
      # 301、302缓存1小时
      proxy_cache_valid any 1m;
      #其他未设置的状态码缓存1分钟
      
      #设置反向代理
      proxy_pass  http://192.168.78.128;
  • $host:服务器的域名,如zhazha.hui.test
  • $uri:域名和参数之间的部分,如/index.html。
  • $is_args: 有URL参数时,则值为?,否则为空字符串。
  • $args:保存URL参数,如a=1&b=2,没有参数时为空字符串。
  • 利用isargsis_args和args,可以实现根据不同URL参数缓存不同文件。 为了便于在浏览器端查看是否正确缓存,可配置 add_header 指令添加了 两个响应消息头。其中X-Via表示服务器地址,利用内置变量serveraddr获取,另一个XCache表示资源缓存状态,利用内置变量server_addr 获取,另一个X-Cache表示资源缓存状态,利用内置变量upstream_cache_status 获取。$upstream_cache_status 的返回值有7个,如下所示:
返回值说明
HIT未命中,请求被传送到后端
MISS未命中,请求被传送到后端
EXPIRED缓存已经过期,请求被传送到后端
UPDATING正在更新缓存,将使用旧的应答
STALE无法从后端服务器更新缓存时,返回了旧的缓存内容(可通过proxy_ cache_ use_stale指令配置)
BYPASS缓存被绕过了(可通过proxy_cache_bypass指令配置)
REV ALIDA TED启用proxy_ cache_ revalidate 指令后,当缓存内容过期时,Nginx通过一次If-Modified- Since的请求头去验证缓存内容是否过期,此时会返回该状态

Ps: 那这永久缓存和临时缓存有啥区别呢?
永久和临时在使用看,配置就不一样,临时是针对 HTTP 作用域,所以整个HTTP下配置的 Server 站点都可以享受,基本适合一切的缓存代理来做,并且它不会一直存在。失效就不用了。
而永久就不一样,它只是针对单个Server来说,符合那种单点业务,比如流量高访问的高的页面,用它直接缓存本地后面即可直接使用。

缓存代理解析过程是如何实现的?
HTTP 请求匹配到server站点后,就直接找到相关 location 规则,然后再向后端源服务器请求数据,得到响应结果并缓存起来。 这里就是核心,缓存代理需先生产缓存后才能去命中缓存,如果都没有那只有去后端源服务里面获取。

缓存为啥要用哈希算法生成缓存目录呢?

因为哈希。它的时间复杂度是O(1)。

如不使用哈希,那每次请求到后端进行缓存检索时,就需从众多的缓存中挨个进行对比,这样查找匹配的效率就变得很低。如果用来哈希算法,每次缓存命中时直接根据请求计算出哈希码,然后再哈希表下面进行对比,这样在众多的缓存内容下检索救护更快速。

那算法的本质是什么?

哎呦,小伙还喜欢问这个。 很多人都感觉算法很难懂,首先你得从大观上来看。这里还得提下 数据结构和算法

算法实质就是解决问题的方案,是一系列解决问题的清晰指令,而数学思想是优化这种方案的武器。

其实不用算法,也会有很多种方式来解决问题,但有了算法它的效率会更快,就好像你做 从1-1000相加的结果

普通方式:从 1+2+3+.....+1000。 这属于毫不讲理型,
算法方式:(1+1000)x(1000/2) 这属于大脑开动型

算法说白了就是用算术解决问题的方法,用一些数学上面的方式去解决特定问题的方案。

而数据结构就是存储数据的一种形式,数据结构本身在访问和处理上的效率就会高于普通的形式,因为它们会按照指定的结构去存储和读取。 所以一般在编写程序都会考虑时间和空间复杂度。 故常会结合这两种东西来解决实际资源消耗大的问题

FastCGI缓存

FastCGI模块主要能够接受PHP-FPM请求响应,而且能够与任何兼容FastCGI协议的服务器通信。而FastCGI缓存只是该模块下的一部分,属于动态资源请求。 而前面临时+永久缓存都针对整个网络请求,并不是FastCGI请求部分,如果有了它,那么将大大减少nginx和PHP的网络通信。

常用指令

  • fastcgi_cache
    • 语法:fastcgi_cache zone_name
    • 说明:用于设置哪个缓存区将被使用,zone_name 的值为 fastcgi_cache_path 指令创建的缓存区名称
    • 作用域:http、server、location 默认值: off
  • fastcgi_cache_path
    • 语法:fastcgi_cache_path path [levels=number] keys_zone=zone_name:zone_size [inactive=time] [max_size=size];
    • 说明:用于设置哪个缓存区将被使用,zone_name 的值为 fastcgi_cache_path 指令创建的缓存区名称。
    • 作用域:http

例如:fastcgi_cache_path /usr/local/nginx/fastcgi_cache_dir levels=1:2 keys_zone=cache_one:500m inactive=1d max_size=30g ;

  • levels 指定该缓存空间有两层hash目录,第一层目录为1个字母,第二层为2个字母,保存的文件名会类似 /usr/local/nginx/fastcgi_cache_dir/c/29/XXXX;
  • keys_zone 参数用来为这个缓存区起名;
  • 500m 指内存缓存空间大小为500MB;
  • inactive 的1d 指如果缓存数据在1天内没有被访问,将被删除;
  • max_size 的30g 是指硬盘缓存空间为30GB
  • fastcgi_cache_methods

    • 语法: fastcgi_cache_methods [GET HEAD POST] ;
    • 说明:该指令用于设置缓存哪些HTTP方法,默认缓存HTTP GET/HEAD 方法,不缓存HTTP POST方法
  • fastcgi_cache_min_uses

    • 语法: fastcgi_cache_min_uses the_number;
    • 说明:该指令用于设置缓存的最小使用次数,默认值为1.
  • fastcgi_cache_valid

    • 语法: fastcgi_cache_valid reply_code [reply_code...] time;
    • 说明: 该指令用于对不同返回状态码的URL设置不同的缓存时间.
  • fastcgi_cache_valid 200 302 10m ;

  • fastcgi_cache_valid 404 1m ;

    • 设置200,302状态的URL缓存10分钟,404状态的URL缓存1分钟.如果不指定状态码,直接指定缓存时间,则只有200,301,302状态的URL缓存5分钟.
  • fastcgi_cache_key

    • 语法:fastcgi_cache_key line ;
    • 该指令用来设置Web缓存的Key值,Nginx根据Key值md5哈希存储缓存.一般根据FastCGI服务器的地址和端口,$request_uri(请求的路径)等变量组合成fastcgi_cache_key。

配置FastCGI缓存

1、编辑nginx.conf

http{  
  #fastcgi_temp_path和fastcgi_cache_path指定的路径必须在同一分区
  fastcgi_temp_path /usr/local/nginx/fastcgi_temp_path ;
  #设置Web缓存区名称为cache_one,内存缓存空间大小为500MB,自动清除超过1天没有被
 
  #访问的缓存数据,硬盘缓存空间大小为30G
  fastcgi_cache_path /usr/local/nginx/fastcgi_cache_path levels=1:2 keys_zone=cache_one:200m inactive=1d max_size=30g ;

} 
2、修改站点配置文件中的fastcgi内容

  server{    
    location ~ .*\.(php|php5)$ {
      #使用Web缓存区cache_one
      fastcgi_cache cache_one ;
      #对不同的HTTP状态码缓存设置不同的缓存时间
      fastcgi_cache_valid 200 10m ;
      fastcgi_cache_valid 301 302 1h ;
      fastcgi_cache_valid an 1m ;
      #设置Web缓存的key值,Nginx根据key值md5哈希存储缓存,这里根据"FastCGI服务 
 
    # "FastCGI器的IP+端口+求的URI" 组合成Key。
      fastcgi_cache_key 127.0.0.1:9000$requet_uri ;
      #FastCGI服务器
      fastcgi_pass 127.0.0.1:9000 ;
      fastcgi_index index.php ;
      include fcgi.conf ;
    }
}

缓存清理配置

Nginx缓存虽减轻了后端服务器的压力,但是会导致文件修改后无法及时更新缓存,只有及时删除服务器中的缓存文件,Nginx才会重新请求后端服务器。 目前,Nginx 官方不支持清理指定URL的缓存,需借助第三方(ngx_cache_purge)模块才可以实现。

1.备份已安装的Nginx

在添加ngx_cache_purge 模块前,关闭Nginx服务,备份已有的Nginx目录。

[root@吒吒辉]# cp -r /usr/local/nginx /usr/1ocal/nginx_old2

2.重新编译安装Nginx

在GitHub平台可以获取ngx_cache_purge模块的源代码,参考下载地址为

https://github.com/FRiCKLE/ngx_cache_purge/tags 

这里选择zip格式的文件。

下载完成后将其上传到root目录下,解压并重命名为ngx_cache_purge,具体命令

[root@ localhost ~ ]# unzip ngx_cache_purge-2.3.zip
[root@ localhost ~]# mv ngx_cache_purge-2.3 /usr/1ocal/ngx_cache_purge

接着,进人 Nginx 文件的解压目录,在./configure时添加对nginx-cachepurge模块的支持,完成Nginx编译选项的配置,具体命令如下。

[root@ localhost ~]# cd nginx-1.10.1
[roote localhost nginx-1.10.1]# ./configure \
#  指定nginx安装目录
--prefix= /usr/local/nginx \
--add-module= /usr/local/ngx_cache_purge \
# 启用https支持。
--with-http_ss1_module 
[roote localhost ~]# make && make install

完成Nginx的编译和安装后,启动Nginx服务,在浏览器中访问测试。如果看到Nginx 的默认欢迎页面表示安装成功。 或者使用 nginx -V命令查看是否按照成功模块

3.配置缓存清理功能

安装完 ngx_cache_purge 模块后,可使用该模块提供的 proxy_cache_purge 指令实现缓存清理。在使用 proxy_cache_purge指令时需要遵循如下规则。

  • 指定的缓存区名称要与 proxy_cache_path 指令中出现的缓存区名称一致。
  • 指定的Key值组成规则,要与 proxy_cache_key 指令设置的规则相同。
  • 清理缓存的location编写位置,要在server块中所有location之前,防止其他正则location提前匹配。

根据前文设置得临时缓存配置,修改Nginx的配置文件nginx.conf,在server块中添加以下用于清理缓存的配置。

1  location ~ /purge(/.* ){
2    allow    192.168.78.1;
3   deny     all;
4  proxy_cache_purge cache_one Shost$1$is_args$args;
5  }
  • 第1行,用于以正则方式匹配用户的请求,请求地址符合/purge/URI形式时,就会清理URI对应的缓存文件。

  • 第2-3行用于指定允许清理缓存的客户端,此处表示仅允许IP地址为 192.168.78.1的客户端清理缓存。

  • 第4行通过proxy_cache_purge 指令,指定名称为 cache_one 的缓存区,和待清理缓存文件的Key值组成规则。其中,1表示location正则表达式中的子模式"(/.)"的匹配结果,对应生成缓存Key时的1表示location正则表达式中的子模式 "(/.*)" 的匹配结果,对应生成缓存Key时的uri

4.访问测试

在内容源服务器的网站根目录中,编写测试文件test.html,具体内容如下。


<DOCTYPE html>
<html>
<head><title>吒吒辉欢迎你!</title></head>
<body><h1> 吒吒辉欢迎你一起入群交流学习</h1>< /body>
</html >

然后,在浏览器中访问 curl zhazha.hui.test/test.html(这里是采用本地的云服务器访问,你可自行更换为外网IP)

此时可以看到哈希后生成的缓存文件名称及目录结构。采用tree 命令查看文件目录树,

注: 安装 yum -y install tree

最后,在浏览器中访问 test.ng.test/ purge/test.html,清理test.html的缓存文件,如下所示。从图中可以看到当前清理的缓存文件的key值,以及存放缓存文件的路径。

为了验证是否成功清理了缓存文件,在缓存服务器中查看缓存目录,已经没有文件

总结:

  • 缓存代理分为动静两大类,通过它可以减少下游服务网络IO的开销、程序处理时间的消耗等好处。
  • 缓存方式为永久、临时缓存2大类,一般使用临时缓存
  • 缓存有失效时间,如果数据需要及时更新,那需删除原来数据

我命由我不由天

如有帮助,欢迎关注、分享。帮帮在互联网浪潮中不断挣扎的男子。微信搜索【莲花童子哪吒】 获取体系化内容,还加我纳入群聊,一起交流学习进步提升。