Nginx之旅-Nginx作为缓存服务器

缓存的目的就是减轻服务端的压力。尽量让请求集中在前端就能取到数据。

缓存类型

根据缓存存放位置不同可以分为不同的缓存

如果存放在服务端则是 服务端缓存,例如:redisMongoDB等。

如果是放在代理层则是 代理缓存,存放的内容是从服务端获取到数据,可直接给客户端使用,例如:Nginx

如果存放在客户端就是客户端缓存(浏览器请求服务器获取到的数据)。

我们看下Nginx作为缓存服务器的流程

image-20181209161231732

当客户端请求数据的时候先看代理缓存中是否存在,如果存在则返回。如果不存在,代理服务器向服务器请求数据并保留一份到缓存,然后返回给客户端。

proxy_cache配置语法

配置proxy_cache之前需要先配置proxy_cache_path 指定缓存文件路径

1
2
3
Syntax:	proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
Default: —
Context: http

之后进行proxy_cache的配置

1
2
3
4
Syntax:	proxy_cache zone | off;
Default:
proxy_cache off;
Context: http, server, location

其中的Zone就是上面定义的path

配置语法–缓存过期周期:

1
2
3
Syntax:	proxy_cache_valid [code ...] time;
Default: —
Context: http, server, location

其中的code是指返回的HTTP状态码

配置语法–缓存的维度:

1
2
3
4
Syntax:	proxy_cache_key string;
Default:
proxy_cache_key $scheme$proxy_host$request_uri;
Context: http, server, location

这个是指将什么样子的请求进行缓存(如上面是协议 主机 请求路径作为key进行缓存)

指令 含义
proxy_cache_path /data/nginx/tmp-test levels=1:2 keys_zone=tmp-test:100m inactive=7d max_size=1000g;
proxy_cache_path 缓存文件路径
levels 设置缓存文件目录层次;levels=1:2 表示两级目录
默认所有缓存文件都放在同一个/path/to/cache下,从而影响缓存的性能,大部分场景推荐使用2级目录来存储缓存文件;
keys_zone 设置缓存名字和共享内存大小
在共享内存中设置一块存储区域来存放缓存的key和metadata(类似使用次数),这样nginx可以快速判断一个request是否命中或者未命中缓存,1m可以存储8000个key,10m可以存储80000个key;
inactive 在指定时间内没人访问则被删除
max_size 最大cache空间,如果不指定,会使用掉所有disk space,当达到配额后,会删除最少使用的cache文件。
每一个proxy_cache_path对应一个ngx_http_file_cache_t结构体。
proxy_cache tmp-test 使用名为tmp-test的缓存配置
proxy_cache_key $uri 定义缓存唯一key,通过唯一key来进行hash存取
proxy_cache_methods 设置缓存哪些HTTP方法
proxy_cache_min_uses 指定请求至少被发送了多少次以上时才缓存,可以防止低频请求被缓存
proxy_cache_bypass 如果指定的任何一个变量值不为空,或者不等于0,nginx就不会查找缓存,直接进行代理转发
proxy_cache_lock/proxy_cache_lock_timeout 当多个客户端同时请求同一份内容时,如果开启proxy_cache_lock(默认off)则只有一个请求被发送至后端;其他请求将等待该内容返回;当第一个请求返回时,其他请求将从缓存中获取内容返回;当第一个请求超过了proxy_cache_lock_timeout超时时间(默认5s),则其他请求将同时请求到后端来获取响应,且响应不会被缓存;启用proxy_cache_lock可以应对雪崩效应。
inactive 未被访问文件在缓存中保留时间,本配置中如果60分钟未被访问则不论状态是否为expired,缓存控制程序会删掉文件,默认为10分钟;“需要注意的是,inactive和expired配置项的含义是不同的,expired只是缓存过期,但不会被删除,inactive是删除指定时间内未被访问的缓存文件”
use_temp_path 临时存储区域。开启状态,会把一个写入缓存的文件,先放入一个临时存储区域,建议关闭,可以避免文件系统中不必要的数据拷贝
proxy_cache 启用proxy cache,指定key_zone
add_header 在给客户端的返回中,增加名为X-Cache-Status的header,其值是缓存命中情况,比如MISS,HIT等等。
proxy_cache_key 设置缓存文件中的key,硬盘中缓存文件的名字key值的MD5。譬如key是test.xnow.me/,则在硬盘上的md5值是c9d71dc81143d6d9a60165bdcb1b9c9f,计算方法: echo -n “test.xnow.me/“
proxy_cache_valid 200 304 301 302 10d: 设置缓存的状态码,把返回状态是200和304的请求缓存起来。缓存时间是60分钟,过了缓存时间之后,设置缓存状态为EXPIRED,这是绝对时间,和上次更新时间相比。
10d代表缓存的时间,为10天
proxy_cache_use_stale 返回码出错的时候,使用缓存数据。譬如出现超时,502和503等等情况

这个小节直接看老师的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
    # 对于服务器进行负载均衡
upstream imooc {
server 116.62.103.228:8001;
server 116.62.103.228:8002;
server 116.62.103.228:8003;
}

proxy_cache_path /opt/app/cache levels=1:2 keys_zone=imooc_cache:10m max_size=10g inactive=60m use_temp_path=off;

server {
listen 80;
server_name localhost;


access_log /var/log/nginx/test_proxy.access.log main;


location / {
proxy_cache imooc_cache; # 设置上面创建的缓存 如果不使用 设置为 off
proxy_pass http://imooc; # 代理
proxy_cache_valid 200 304 12h; # 对于服务器返回200 304 缓存12个小时
proxy_cache_valid any 10m; # 其他所有返回是10分钟过期
proxy_cache_key $host$uri$is_args$args; # $uri: 请求的URI,可能和最初的值不同,比如经过重定向之类的 $is_args: 请求行中是否匹配参数,如果有参数返回?,没有返回空字符串 args: 请求中的参数
add_header Nginx-Cache "$upstream_cache_status";

proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504; # 负载均衡 中 当其中一台服务器有出现 500错误 超时 错误 无效的头信息的时候 直接跳过 访问下一台服务器
include proxy_params; # 为之前我们设置过的 代理配置
}

}

如何清理指定缓存?

方式一:rm -rf清除缓存目录内容

方式二:第三方扩展模块ngx_cache_purge

如何让部分页面不缓存?

1
2
3
4
5
6
7
8
9
10
# 通过 proxy_no_cache 设置不缓存

if ($request_uri ~ ^/(url3|login|register|password\/reset)) {
set $cookie_nocache 1;
}

location / {
proxy_no_cache $cookie_nocache $arg_nocache $arg_comment;
proxy_no_cache $http_pragma $http_authorization;
}

配置项–proxy_cache_bypass

1
2
3
4
Syntax:	proxy_no_cache string ...;
Default: —
Context: http, server, location
# 作用: 可以指定不缓存URL

该指令用于定义满足条件的响应不会被保存到缓存中。在条件字符串中至少有一个条件不为空或者0,符合这样条件的响应才不会被缓存。举例如下:

1
2
proxy_no_cache $cookie_nocache $arg_nocache$arg_comment;
proxy_no_cache $http_pragma $http_authorization;

其中,cookie_nocachearg_nocache...皆为变量,可以根据你访问的匹配策略来设置,其值只有2类,0和非0;

访问匹配策略例如:

1
2
3
if ($request_uri ~ ^/(login|register|password\/reset)/) { 
set $cookie_nocache 1;
}

如果在此链式配置中,只要有一个值不为0,则不会cache;例如:

1
proxy_no_cache $cookie_nocache(0) $arg_nocache(1) $arg_comment(0)

则不会被cache。

注:一般会配合proxy_cache_bypass共同使用;

配置项–proxy_cache_bypass

1
2
3
Syntax: proxy_cache_bypass string ...;
Default: —
Context: http , server , location

我们可以指定变量,当变量的值不为空或者不为0就不会缓存。

该指令用于定义哪些情况不从cache读取,直接从backend获取资源;配置方式同proxy_no_cache

缓存命中分析

方式一: 通过设置response的头信息Nginx-Cache

add_header Nginx-Cache "$upstream_cache_status";

方式二:通过设置log_format打印日志分析

​ 将变量upstream_cache_status配置到nginx的访问日志来查看是否命中缓存。

看下upstream_cache_status的展示信息

状态 意义
MISS 未命中,请求被传送到后台处理
HIT 缓存命中
EXPIRED 缓存已经过期,请求被传送到后台处理
UPDATING 正在更新缓存,将使用旧的应答
STALE 后端得到过期的应答
1
2
3
4
5
6
7
location / {
proxy_cache imooc_cache;
add_header Nginx-Cache $upstream_cache_status;

proxy_next_upstream error timeout invalid_header http_500 http_502 http_503 http_504;
include proxy_params;
}

我们在返回中新增了response头信息 Nginx-Cache,可以在请求返回的头中查看该头信息的值来判断是否命中缓存,命中的话该值为HIT

如果我们想根据日志进行分析命中了 需要先将变量添加到log_format中。

1
2
3
4
log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"'
' "$upstream_cache_status"';

日志分析命中率

大文件分片请求
1
2
3
Syntax:	slice size;
Default: slice 0;
Context: http, server, location

可以将大文件进行分片请求,即将一个大的文件请求分成多个小的切片。

Bhvim4.png

在前端传过来一个大文件请求的时候,第一次会去请求文件大小,然后结合我们设置的切片大小进行分割成多个小的请求。多个小的请求到前端都是独立的文件。

优势:

每个子请求收到的数据都会形成一个独立文件,一个请求断了,其他请求不受影响。

缺点:

当文件很大或者slice很小的时候,可能会导致文件描述符耗尽等情况。

一次请求就占有操作系统的一个句柄和建立一次连接

知识就是财富
如果您觉得文章对您有帮助, 欢迎请我喝杯水!