缓存基础

缓存分类

某些场景下,Nginx需要通过worker到上有服务中获取数据并将结果响应给客户端,在高并发场景下,我们完全可以将这些数据视为热点数据,并将其缓存到Nginx服务上。

  1. 客户端缓存:将缓存数据放到客户端。
    优点:数据直接本地获取,响应快,无网络开销
    缺点:仅对单一用户生效

  2. 服务端缓存:将数据缓存到服务端
    优点:有效降低上有服务器压力,且对所有用户生效。
    缺点: 仍然存在网络开销。

所以综合考虑上述的优缺点,我们建议合理利用缓存,必要时同时开启客户端和服务端的缓存。

缓存相关指令及参数含义

proxy_cache

语法: proxy_cache zone |off;
默认值:proxy_cache off;
上下文: http、server、location
作用:在Nginx中实现反向代理响应的缓存功能

proxy_cache_path

语法:proxy_cache_path path [levels=levels] keys_zone=name:size [inactive=time1] [max_size=size2] [loader_files=number] [loader_sleep=time2] [loader_threshold=time3];
默认值:proxy_cache_path:off;
上下文:http
参数含义:

  1. path:缓存文件存放路径
  2. level: path的目录层级,例如2:2 ,那么目录就会类似:
/path/to/cache/00/path/to/cache/01/path/to/cache/10/path/to/cache/11
  1. use_temp_path:若开启则使用proxy_temp_path的路径
  2. key_zone:name是共享名称,size是共享内存的大小
  3. inactive:共享内存的值在规定时间内没有被访问就会被清理,默认配置为10min
  4. max_size: 设定最大的缓存文件大小,超过将有CM(cache manager)清理
  5. manager_files:CM一次清理的最大缓存文件数,默认为100
  6. manager_sleep:CM清理一次后进程的休眠时间,默认200毫秒
  7. manager_threshold:CM一次最长耗时,默认50ms
  8. loader_files:CL载入文件到共享内存每批最多的文件数,默认为100
  9. loader_sleep:CL加载文件到内存后,进程进入休眠时间,默认200毫秒
  10. loader_threshold:CL每次载入文件到共享内存的最大耗时,默认50毫秒

proxy_cache_key

语法:proxy_cache_key stirng;
默认值: proxy_cache_key $scheme%proxy_host$request_uir
上下文:http、server、location
作用:指定生成缓存键的方式

proxy_cache_valid

语法:proxy_cache_valid [code] time
默认值:无
上下文:http、server、location
配置示例: proxy_cache_valid 60m;
作用:设置缓存的有效时间

upstream_cache_status

作用:用于获取反向代理请求是否命中缓存。

状态值:

  1. MISS:未命中缓存
  2. HIT:命中缓存
  3. EXPIRED:缓存过期
  4. STALE:命中了陈旧的缓存
  5. REVALIDDATED:nginx验证陈旧缓存依然有效
  6. UPDATING:内容陈旧,但正在更新
  7. BYPASS:响应从原始服务器获取

我们只需按照下面所示的配置即可打印对应的状态值:

location / {proxy_cache my_cache;proxy_pass http://backend;proxy_set_header X-Cache-Status $upstream_cache_status;...}

配置示例

基础示例

如下图,我们会访问服务器1,服务器1的nginx配置了负载均衡,会视情况访问上游nginx服务器的1010或1011端口

我们先给出不走缓存的基础配置,配置内容比较简单,即轮询不同的上游服务器。

http {include mime.types;default_typeapplication/octet-stream;# 轮询上游服务器两个端口 upstream cache_server {server 192.168.12.128:1010;server 192.168.12.128:1011; }sendfileon;keepalive_timeout65;server {listen 80;server_namelocalhost;location / {proxy_pass http://cache_server; }}

测试结果,可以看到请求结果每次都会交替指向不同端口的网页内容(1010,1011分别对应上述两个上有服务器两个端口的网页)

[root@localhost ~]# curl 192.168.12.127:801010[root@localhost ~]# curl 192.168.12.127:801011[root@localhost ~]# curl 192.168.12.127:801010[root@localhost ~]# curl 192.168.12.127:801011[root@localhost ~]# curl 192.168.12.127:801010[root@localhost ~]# curl 192.168.12.127:801011

因为这些文件资源,比较固定,所以我们希望nginx能够将这些网页缓存起来,于是我们给出走缓存的配置。
需要注意的是,配置完缓存相关的配置之后,笔者为了能够更直观的看到缓存命中结果,在http的头部添加了Nginx-Cache-Status的值。

http {include mime.types;default_typeapplication/octet-stream;# 缓存配置,指定缓存路径、每个目录有2个层级,每个层级有两个目录、定义缓存键的共享内存区域名为cache_zone 为30m,最大值为32m,60min后缓存未被使用则会被删除,并关闭临时文件存储 proxy_cache_path /usr/local/nginx/cache_temp levels=2:2 keys_zone=cache_zone:30m max_size=32m inactive=60m use_temp_path=off; upstream cache_server {server 192.168.12.128:1010;server 192.168.12.128:1011; }sendfileon;keepalive_timeout65;server {listen 80;server_namelocalhost;location / {# 引用上文的缓存配置 proxy_cache cache_zone;# 仅对响应200的有效,保存5minproxy_cache_valid 200 5m;# http请求头记录缓存状态add_header Nginx-Cache-Status "$upstream_cache_status";proxy_pass http://cache_server; }}

完成后我们重载一下配置,然后尝试发起请求。可以看到我们往请求头中放的Nginx-Cache-StatusMISS变为HIT

我们键入curl命令发起第一个请求,发送一个HEAD请求,并输出服务器返回的HTTP响应头信息,而不包括响应体

curl 192.168.12.127:80 -I

从结果来看,第一次的并没有命中缓存,所以Nginx-Cache-Status返回MISS。

HTTP/1.1 200 OKServer: nginx/1.10.2Date: Tue, 19 Jul 2022 15:52:18 GMTContent-Type: text/htmlContent-Length: 5Connection: keep-aliveLast-Modified: Tue, 19 Jul 2022 15:23:53 GMTETag: "62d6cc89-5"Nginx-Cache-Status: MISSAccept-Ranges: bytes

再一次curl后就会发现结果变为HIT,由此可知上一次将请求结果缓存,所以本次请求命中了缓存。

HTTP/1.1 200 OKServer: nginx/1.10.2Date: Tue, 19 Jul 2022 15:52:19 GMTContent-Type: text/htmlContent-Length: 5Connection: keep-aliveLast-Modified: Tue, 19 Jul 2022 15:23:53 GMTETag: "62d6cc89-5"Nginx-Cache-Status: HITAccept-Ranges: bytes

同样的,我们也可以通过查看缓存文件夹看到我们缓存的文件

cat /usr/local/nginx/cache_temp/76/d1/608ccf55afd7b17fc84b73d835ecd176

可以看到缓存内容如下,这就是对应的html网页的参数和内容。

^▒▒b▒▒▒b2▒▒b▒b▒▒▒▒"62d6cc89-5"KEY: http://cache_server/HTTP/1.1 200 OKServer: nginx/1.10.2Date: Tue, 19 Jul 2022 15:52:18 GMTContent-Type: text/htmlContent-Length: 5Last-Modified: Tue, 19 Jul 2022 15:23:53 GMTConnection: closeETag: "62d6cc89-5"Accept-Ranges: bytes1010

配置nginx不缓存特定内容

有时候我们希望缓存可以移除一些没必要的内容,我们就可以基于这些参数做到不缓存某些内容。

proxy_no_cache

语法:proxy_no_cache string;
默认值:无
上下文:http、server、location
作用:用于判断是否禁用缓存的条件表达式

proxy_cahce_bypass

语法:proxy_cache_bypass string;
默认值:无
上下文:http、server、location
作用:在特定条件下绕过缓存并直接向上游服务器发送请求

配置示例

我们希望txt、text文件不被缓存。对应nginx配置如下,我们只需判断用户请求文件是否是txt、text,如果是则添加一个标志变量 $cookie_name"no cahe",然后proxy_no_cache 看到当前请求存在这个$cookie_name变量,就不缓存,配置文件示例如下:

http {include mime.types;default_typeapplication/octet-stream; proxy_cache_path /usr/local/nginx/cache_temp levels=2:2 keys_zone=cache_zone:30m max_size=32m inactive=60m use_temp_path=off; upstream cache_server {server 192.168.12.128:1010;server 192.168.12.128:1011; }sendfileon;keepalive_timeout65;server {listen 80;server_namelocalhost; # 如果是txt text文件就加个cookir的变量if ($request_uri ~ \.(txt|text)$ ) { set $cookie_name "no cahe";}location / {# 引用上文的缓存配置 proxy_cache cache_zone;# 如果有cookie_name这个变量就不换缓存proxy_no_cache $cookie_name;# 仅对响应200的有效proxy_cache_valid 200 5m;# http请求头记录缓存状态add_header Nginx-Cache-Status "$upstream_cache_status";proxy_pass http://cache_server; }}

测试结果如下可以看到3次curl的及如果都是MISS索命txt文件的请求都不走缓存。

第一次curl请求,返回miss。

[root@localhost ~]# curl 192.168.12.127:80/index.txt -IHTTP/1.1 200 OKServer: nginx/1.10.2Date: Tue, 19 Jul 2022 16:26:03 GMTContent-Type: text/plainContent-Length: 5Connection: keep-aliveLast-Modified: Tue, 19 Jul 2022 16:25:30 GMTETag: "62d6dafa-5"Nginx-Cache-Status: MISSAccept-Ranges: bytes

第二次请求,还是返回miss。

[root@localhost ~]# curl 192.168.12.127:80/index.txt -IHTTP/1.1 200 OKServer: nginx/1.10.2Date: Tue, 19 Jul 2022 16:26:04 GMTContent-Type: text/plainContent-Length: 5Connection: keep-aliveLast-Modified: Tue, 19 Jul 2022 16:25:17 GMTETag: "62d6daed-5"Nginx-Cache-Status: MISSAccept-Ranges: bytes

缓存失效降低上游压力机制

合并源请求

如下图,三个相同的请求到达nginx,合并源请求做的就是让红色的请求去应用服务器获取结果,其他两个请求排队等待,当红色请求得到结果并缓存到nginx时,他们就不需要去应用服务器拿结果了,直接从缓存中拿到请求结果并返回即可。

相关配置

proxy_cache_lock

当多个客户端请求一个缓存中不存在的文件(或称之为一个MISS),只有这些请求中的第一个被允许发送至服务器。其他请求在第一个请求得到满意结果之后在缓存中得到文件。如果不启用proxy_cache_lock,则所有在缓存中找不到文件的请求都会直接与服务器通信。

语法:proxy_cache_lock on|off;默认值:无上下文:http、server、location
proxy_cache_lock_age
语法:proxy_cache_lock_age time;默认值:proxy_cache_lock_age5s;上下文:http、server、location作用:直缓存锁的有效期
proxy_cache_lock_timeout
语法:proxy_cache_lock_timeout time;默认值:proxy_cache_lock_timeout 5s;上下文:http、server、location作用:指定等待获取缓存锁的超时时间

配置示例

具体配置笔者已详尽注释到下文中,读者可自行参阅。

http {include mime.types;default_typeapplication/octet-stream; proxy_cache_path /usr/local/nginx/cache_temp levels=2:2 keys_zone=cache_zone:30m max_size=32m inactive=60m use_temp_path=off; upstream cache_server {server 192.168.12.128:1010;server 192.168.12.128:1011; }sendfileon;keepalive_timeout65;server {listen 80;server_namelocalhost; # 如果是txt text文件就加个cookir的变量if ($request_uri ~ \.(txt|text)$ ) { set $cookie_name "no cahe";}location / {# 引用上文的缓存配置 proxy_cache cache_zone;# 如果有cookie_name这个变量就不换缓存proxy_no_cache $cookie_name;# 仅对响应200的有效proxy_cache_valid 200 5m;# http请求头记录缓存状态add_header Nginx-Cache-Status "$upstream_cache_status";# 开启lockproxy_cache_lock on;# 等待锁超时时间为5sproxy_cache_lock_timeout 5s;# 锁有效期为5sproxy_cache_lock_age 5s; proxy_pass http://cache_server; }}

启用陈旧请求

原理

如下图,当大量请求到达服务器亦或者上游服务器宕机等问题出现时,nginx支持将陈旧缓存先返回给前端,这种方案使用于对数据一致性要求不高的业务场景。

关键配置

  1. proxy_cache_use_stale:当用户无法从原始请求中获取响应或者上游服务器宕机时使用,将陈旧的缓存内容返回给前端
proxy_cache_use_stale error | timeout | invalid_header |updating | http_500 | http_502 | http_503 | http_504 |http_403 | http_404 | http_429 | off ...;默认值: proxy_cache_use_stale off;上下文: http, server, location可选参数内容含义1. error:与上游建立连接、发送请求、读取响应头出错2. timeout:与商友建立连接、发送请求、读取响应头超时3. invalid_header:无效头部4. updating:缓存过期,正在更新时5. http_500:返回状态码5006. http_502:返回状态码5007. http_503:返回状态码5008. http_504:返回状态码500.......
  1. proxy_cache_backgroud_update:新的缓存更新操作在后台运行
语法:proxy_cache_backgroud_update on | off;默认值:proxy_cache_backgroud_update off;上下文: http、server、location

配置示例

配置实例如下所示,读者可参考注释阅读。

server {listen 80;server_namelocalhost;#charset koi8-r;#access_loglogs/host.access.logmain;location / {root html;indexindex.html index.htm; # 缓存更新在后台完成proxy_cache_background_update on;# http响应超时 错误 缓存更新时返回陈旧缓存proxy_cache_use_stale errortimeout updating;}}

第三方清除缓存模块

我们在上面往缓存中添加了一堆缓存,有时我们希望能够将其删除,我们就可以使用第三方模块进行缓存删除

首先我们需要下载第三方缓存清除模块

wget http://labs.frickle.com/files/ngx_cache_purge-2.3.tar.gz

进入nginx安装文件目录重新编译文件,然后再make、make install

./configure --add-module=ngx_cache_purge-2.3 ;make;make install

修改配置文件,如下所示,我们使用clear/需要被清除的文件的资源名配置如下,例如我们的请求将index.html缓存(proxy_cache_key的配置会将这个缓存结果以host+uri作为key将资源缓存起来),那么我们就可以使用/clear/inedx.html将其清除。
注意配置完成后需要将缓存目录清楚,再reload一下。

http {include mime.types;default_typeapplication/octet-stream; proxy_cache_path /usr/local/nginx/cache_temp levels=2:2 keys_zone=cache_zone:30m max_size=32m inactive=60m use_temp_path=off; upstream cache_server {server 192.168.12.128:1010;server 192.168.12.128:1011; }sendfileon;keepalive_timeout65;server {listen 80;server_namelocalhost; # 如果是txt text文件就加个cookir的变量if ($request_uri ~ \.(txt|text)$ ) { set $cookie_name "no cahe";}location ~/clear(/.*){# 清楚host+映射变量的缓存proxy_cache_purge cache_zone $host$1;}location / {# 引用上文的缓存配置 proxy_cache cache_zone;# 缓存值的keyproxy_cache_key $host$uri;# 如果有cookie_name这个变量就不换缓存proxy_no_cache $cookie_name;# 仅对响应200的有效proxy_cache_valid 200 5m;# http请求头记录缓存状态add_header Nginx-Cache-Status "$upstream_cache_status";# 开启lockproxy_cache_lock on;# 设置持有锁的超时时间proxy_cache_lock_timeout 5s;# 超时后多久再尝试proxy_cache_lock_age 5s;proxy_pass http://cache_server; }error_page 500 502 503 504/50x.html;location = /50x.html {root html;}}}

完成配置之后,我们不妨重载一下配置进行测试,键入如下命令进行第一次请求:

curl 127.0.0.1:80/index.html -I

可以看到第一次结果为MISS

HTTP/1.1 200 OKServer: nginx/1.10.2Date: Wed, 20 Jul 2022 13:36:52 GMTContent-Type: text/htmlContent-Length: 5Connection: keep-aliveLast-Modified: Tue, 19 Jul 2022 15:24:44 GMTETag: "62d6ccbc-5"Nginx-Cache-Status: MISSAccept-Ranges: bytes

再次请求,HIT命中缓存

HTTP/1.1 200 OKServer: nginx/1.10.2Date: Wed, 20 Jul 2022 13:36:54 GMTContent-Type: text/htmlContent-Length: 5Connection: keep-aliveLast-Modified: Tue, 19 Jul 2022 15:24:44 GMTETag: "62d6ccbc-5"Nginx-Cache-Status: HITAccept-Ranges: bytes

然后我们使用缓存清除地址将其清除

curl 127.0.0.1:80/clear/index.html -I
HTTP/1.1 200 OKServer: nginx/1.10.2Date: Wed, 20 Jul 2022 13:36:57 GMTContent-Type: text/htmlContent-Length: 287Connection: keep-alive

再次发起请求发现,又变成MISS状态了。

HTTP/1.1 200 OKServer: nginx/1.10.2Date: Wed, 20 Jul 2022 13:37:02 GMTContent-Type: text/htmlContent-Length: 5Connection: keep-aliveLast-Modified: Tue, 19 Jul 2022 15:23:53 GMTETag: "62d6cc89-5"Nginx-Cache-Status: MISSAccept-Ranges: bytes

基于nginx配置https请求

简介

关于https可以看到笔者这篇文章,做了非常详细的介绍,总的来说https就是客户端和服务端通过非对称密钥协商出一个对称密钥来保证数据安全,进而保证后续数据交互安全又高效。

http知识总结

csr/crt/key/pem的区别

  1. csr全称是certificate signing request,是向CA去申请证书的请求文件的后缀,其中包含了服务器的公钥和一些基本信息。
  2. crt是certificate的简写,就是最后拿到的证书文件的后缀。
  3. key是密钥的后缀,不同工具有不同编码方式,pem后缀的文件也有可能是证书或者密钥。

配置示例

由于我们没有ca,所以我们这里就自己在自己的服务器生成的key文件,模拟ca的私钥。

openssl genrsa -out ca.key 1024

然后在生成一个向ca申请证书的文件csr

openssl req -new -key ca.key -out ca.csr

使用key文件配置csr文件生成一个crt文件(模拟ca为服务器签发证书)

openssl x509 -req -days 365 -in ca.csr -signkey ca.key -out ca.crt

nginx配置,注意刚刚的crt文件和key文件都需要放到nginx的conf文件夹

 server {listen 443 ssl;server_namelocalhost;ssl_certificateca.crt;ssl_certificate_keyca.key;ssl_session_cacheshared:SSL:1m;ssl_session_timeout5m;ssl_ciphersHIGH:!aNULL:!MD5;ssl_prefer_server_cipherson;location / {root html;indexindex.html index.htm;}}

测试

完成配置后,重载一下配置进入测试,可以发现HTTPS虽然不受信任,但是可以使用了。

参考文献

【Django 030】部署到Nginx之配置https:https://blog.csdn.net/Victor2code/article/details/106485814)