文章目录
- 什么是CDN?
- 为什么需要 内网CDN/edge cache?
- Nginx配置
- Edge Cache服务配置
- 简单静态文件缓存服务配置
- 缓存控制
- $upstream_cache_status
- Gzip压缩
- 参考
什么是CDN?
首先要明白什么是CDN?
CDN英文全称Content Delivery Network,中文翻译即为内容分发网络。它是建立并覆盖在承载网之上,由不同区域的服务器组成的分布式网络。将源站资源缓存到全国各地的边缘服务器,供用户就近获取,降低源站压力。
CDN应用广泛,支持多种行业、多种场景内容加速,例如:图片小文件、大文件下载、视音频点播、直播流媒体、全站加速、安全加速。
加速原理如下
以下内容来着阿里云
假设您的加速域名为www.aliyundoc.com
,接入CDN开始加速服务后,当终端用户在北京发起HTTP请求时,处理流程如下图所示。
- 当终端用户向
www.aliyundoc.com
下的指定资源发起请求时,首先向Local DNS(本地DNS)发起请求域名www.aliyundoc.com
对应的IP。 - Local DNS检查缓存中是否有
www.aliyundoc.com
的IP地址记录。如果有,则直接返回给终端用户;如果没有,则向网站授权DNS请求域名www.aliyundoc.com
的解析记录。 - 当网站授权DNS解析
www.aliyundoc.com
后,返回域名的CNAMEwww.aliyundoc.com.example.com
。 - Local DNS向阿里云CDN的DNS调度系统请求域名
www.aliyundoc.com.example.com
的解析记录,阿里云CDN的DNS调度系统将为其分配最佳节点IP地址。 - Local DNS获取阿里云CDN的DNS调度系统返回的最佳节点IP地址。
- Local DNS将最佳节点IP地址返回给用户,用户获取到最佳节点IP地址。
- 用户向最佳节点IP地址发起对该资源的访问请求。
- 如果该最佳节点已缓存该资源,则会将请求的资源直接返回给用户(步骤8),此时请求结束。
- 如果该最佳节点未缓存该资源或者缓存的资源已经失效,则节点将会向源站发起对该资源的请求。获取源站资源后结合用户自定义配置的缓存策略,将资源缓存到CDN节点并返回给用户(步骤8),此时请求结束。
从这个例子可以了解到:
- CDN的加速资源是跟域名绑定的。
- 通过域名访问资源,首先是通过DNS查找离用户最近的CDN节点(边缘服务器)的IP
- 通过IP访问实际资源时,如果CDN上并没有缓存资源,则会到源站请求资源,并缓存到CDN节点上,这样,用户下一次访问时,该CDN节点就会有对应资源的缓存了。
简单讲,CDN就是通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容。
CDN主要解决什么问题?
- 物理距离远,多次网络转发,延时高不稳定;
- 所在运营商不同,需运营商之间转发绕行;
- 高并发访问时,网络带宽处理能力有限,海量请求时,响应速度与可用性降低。
- 高并发下载或者下载突增场景下对源站性能要求非常高,且源站的带宽成本也较高。
为什么需要 内网CDN/edge cache?
我来说一种场景,当大型公司或者学校等大型单位在观看直播时,其总的出口带宽是有限的,当有很多人同时在看直播时会把总出口带宽拉满。
优化前
通过中间加个cache层优化后
Nginx配置
我们使用Nginx作为edge cache,配置NGINX的缓存策略就可以,不需要额外插件,NGINX本身就支持:
Edge Cache服务配置
启用基本缓存只需要两个指令:proxy_cache_path
和proxy_cache
。
worker_processes auto; # 建议设置为cpu核心数 grep processor /proc/cpuinfo | wc -l
events{
worker_connections 1024; # 配置可以同时处理的连接数,注意文件描述符的限制
}
http {
keepalive_timeout 65; # 对于高流量的服务,最好设置一个较低的超时值,以便释放等待新请求的连接。
# For Proxy Cache.
# 用于缓存位置和缓存验证时间
proxy_cache_path /path/to/cache levels=1:2 keys_zone=nginx_cache:10m max_size=10g inactive=60m use_temp_path=off;
#proxy_temp_path /tmp/nginx-cache/tmp;
server {
listen 8085;
#定义缓存数据的有效期
proxy_cache_valid 404 10s;
# 如果多个客户端请求缓存中不存在的文件,
proxy_cache_lock on;
proxy_cache_lock_age 300s;
proxy_cache_lock_timeout 300s;
# 定一个文件至少需要被用户访问多少次以后,才会被缓存,默认1
proxy_cache_min_uses 1;
proxy_cache_methods GET HEAD POST;
location ~ /.+/.*\.(m3u8)$ {
proxy_pass http://hls;
# For Proxy Cache.
proxy_cache nginx_cache;
# 指定缓存的key的名称
proxy_cache_key $scheme$proxy_host$uri$args;
# 如果 NGINX从源服务器接收到error、timeout或任何指定的错误,并且它的缓存中有请求文件的陈旧版本,它会传送陈旧文件而不是将错误中继到客户端。
#proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
proxy_cache_valid 200 302 10s;
}
location ~ /.+/.*\.(ts)$ {
proxy_pass http://hls;
# For Proxy Cache.
proxy_cache nginx_cache;
proxy_cache_bypass $http_cache_control;
proxy_cache_key $scheme$proxy_host$uri;
proxy_cache_valid 200 302 60m;
proxy_read_timeout 120s;
proxy_cache_lock on;
proxy_cache_min_uses 3;
proxy_cache_revalidate on;
# 可以在响应header中查看是否名字缓存
add_header X-Proxy-Cache $upsteam_cache_status;
}
}
upstream hls {
server www.laker.com; #原始的源网站地址
server host2.example.com;
}
}
proxy_cache_path
指令的参数
/path/to/cache/
:缓存的本地磁盘目录levels
:**在/path/to/cache/**下设置两级目录层次结构。在单个目录中拥有大量文件会减慢文件访问速度,因此我们建议对大多数部署使用两级目录层次结构。如果levels
不包含该参数,NGINX 会将所有文件放在同一目录中。
- Nginx会基于一个键值(在下面有定义)哈希生成一个缓存键(cache key),该键的最后一个字母作为一级目录名,倒数第二、三个字母作为二级目录名。
1:2
1:表示一级目录可以由1个字符来构成,2:表示二级目录可以由2个字符来构成示缓存结果示例:/data/nginx/cache/c/29/c86156f7dcfecf44876ca30d1bac7febkeys_zone
:设置一个共享内存区域来存储缓存键和元数据。在内存中拥有key的副本使 NGINX 能够快速确定请求是 HIT 还是 MISS,而无需转到磁盘,从而大大加快了检查速度。 一个 1 MB 的区域可以存储大约 8,000 个键的数据,因此示例中配置的 10 MB 区域可以存储大约 80,000 个键的数据。max_size
:设置缓存大小的上限(本例中为 10 GB)。它是可选的;不指定值允许缓存增长以使用所有可用磁盘空间。当缓存大小达到限制时,一个称为缓存管理器的进程会删除最近最少使用的文件,以使缓存大小恢复到限制以下。inactive
:指定项目在不被访问的情况下可以保留在缓存中的时间。在此示例中,缓存管理器进程会自动从缓存中删除 60 分钟未请求的文件,而不管它是否已过期。默认值为 10 分钟 (10m
)。无效内容不同于过期内容。NGINX 不会自动删除缓存控制标头定义的已过期内容(Cache-Control:max-age=120
)。过期(陈旧)的内容只有在 指定的时间内未被访问时才会被删除inactive
。当访问过期内容时,NGINX 会从源服务器刷新它并重置inactive
计时器。use_temp_path=off
:NGINX 首先将要缓存的文件写入一个临时存储区,off
指示NGINX 将它们写入将要缓存的相同目录。我们建议您将此参数设置为off
以避免在文件系统之间进行不必要的数据复制。
proxy_cache_key "$scheme$request_method$host$request_uri$is_args$args"
:定义了一个键值,即上述用于区分目录用的键值。通过该键值,Nginx可以判断一个客户端请求需要的内容是否可以直接从缓存里找给它。对于这个键值,我们用scheme(http或https)、HTTP请求方式、host和URI的组合来创建。
proxy_cache_valid
:条目可以多次设置,定义缓存数据的有效期,时间长短取决于状态码(status code)。我们在这里为success(200 成功)和redirect(302 转发)保存10分钟的缓存,404的缓存1分钟。proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
proxy_cache_bypass
:在这里设置为$http_cache_control
变量,该变量包含一个指示符,说明客户端请求是否明确要求返回一个非缓存的“新鲜”结果。如果是,则Nginx不会从缓存区找回复给客户端,而是按照客户端的要求,从后端取新回复。这里不需要做其他的设置。
X-Proxy-Cache
:将其设置为$upstream_cache_status
。简单来说,这个变量显示了一个请求是命中了缓存、没命中缓存、还是被指定不使用缓存。这对于debug而言比较有用,对于客户端也有一些价值。
简单静态文件缓存服务配置
对于站点中不经常修改的静态内容(如图片,JS,CSS),可以在服务器中设置expires过期时间,控制浏览器缓存,达到有效减小带宽流量,降低服务器压力的目的。
http {
proxy_cache_path /home/cache/data levels=1:2 keys_zone=static:1000m inactive=600m max_size=50G;
proxy_temp_path /home/cache/temp;
server {
location ~ ^/(img|css|js|scripts|stylesheets|uploads)/ {
expires 1d;
access_log off;
proxy_buffering on;
proxy_cache static;
proxy_cache_key "$host$request_uri$is_args$args";
proxy_ignore_headers "Cache-Control" "Expires";
proxy_cache_min_uses 1;
proxy_cache_valid 200 24h;
proxy_cache_use_stale http_502 http_503 http_504;
proxy_hide_header Set-Cookie;
add_header X-Cache "$upstream_cache_status";
// ...
root /data/static/webapp;
}
location ~* ^.+\.(ico|gif|jpg|jpeg|png)$ {
# 关闭日志
access_log off;
#过期时间为30天,
#图片文件不怎么更新,过期可以设大一点,
#如果频繁更新,则可以设置得小一点。
expires 30d;
}
location ~ .*\.(js|css)$ {
expires 10d;
}
缓存控制
不缓存配置
在常用的缓存设置里面有两种方式,都是使用add_header来设置:分别为Cache-Control和Pragma。
location ~ .*\.(css|js|swf|php|htm|html )$ {
add_header Cache-Control no-store;
add_header Pragma no-cache;
}
http缓存规则由响应首部字段进行控制,其中的关键字段有Expires,Cache-Control ,Last-Modified ,Etag 四个字段,Expires和Cache-Control用来确定确定缓存的存储时间,Last-Modified 和Etag则用来确定缓存是否要被更新,我们简单来看一下区别。
- expires: HTTP1.0中用来控制缓存时间的参数,响应头包含日期/时间, 即在此时间之后,响应过期。
- cache-control: HTTP1.1中用来控制缓存时间的参数
- public: 表明响应可以被任何对象(包括:发送请求的客户端,代理服务器,等等)缓存。
- private: 表明响应只能被单个用户缓存,不能作为共享缓存(即代理服务器不能缓存它)。
- max-age=: 设置缓存存储的最大周期,相对于请求的时间缓存seconds秒,在此时间内,访问资源直接读取本地缓存,不向服务器发出请求。(与expires同时出现时,max-age优先级更高)
- s-maxage=: 规则等同max-age,覆盖max-age 或者 Expires 头,但是仅适用于共享缓存(比如各个代理),并且私有缓存中它被忽略。(与expires或max-age同时出现时,s-maxage优先级更高)
- no-store: 不缓存服务器响应的任何内容,每次访问资源都需要服务器完整响应
- no-cache: 缓存资源,但立即过期,每次请求都需要跟服务器对比验证资源是否被修改。(等同于max-age=0)
- Last-modified: 源头服务器认定的资源做出修改的日期及时间。精确度比Etag低。包含有If-Modified-Since或 If-Unmodified-Since首部的条件请求会使用这个字段。
- Etag: HTTP响应头是资源的特定版本的标识符。
判断资源是否更新,需要客户端与服务器共同协作,客户端在首次拿到资源缓存后会存储Etag(若有)和Last-Modified(若有),在下次缓存过期时会将Etag写在请求头部中的If-None-Match中,将Last-Modified值写在请求头部中的If-Modified-Since中,服务端优先对Etag进行对比,然后再对比Last-Modified,完全通过后即视为缓存没有修改,有一项不通过则认为资源已被修改,缓存失效。
不同刷新的请求执行过程
- 浏览器输入 URL 后回车:浏览器如果发现缓存中已经有这个文件了,则不继续请求,直接取缓存,所以速度最快。
- F5:使用协商缓存,使用
If-Modify-since
询问服务器资源是否过期。 - Ctrl+F5:强制刷新,会先把缓存清理掉,再去请求资源。
$upstream_cache_status
MISS
– 在缓存中未找到响应,因此是从源服务器获取的。然后响应可能已被缓存。BYPASS
– 响应是从原始服务器获取的,而不是从缓存中获取的,因为请求与指令匹配proxy_cache_bypass
然后响应可能已被缓存。EXPIRED
– 缓存中的条目已过期。响应包含来自源服务器的新内容。STALE
– 内容陈旧,因为原始服务器未正确响应,并且proxy_cache_use_stale
已配置。UPDATING
– 内容过时,因为条目当前正在更新以响应先前的请求,并且proxy_cache_use_stale updating
已配置。REVALIDATED
– 该proxy_cache_revalidate
指令已启用,NGINX 验证当前缓存的内容是否仍然有效(If-Modified-Since
或If-None-Match
)。HIT
– 响应包含直接来自缓存的有效、新鲜的内容。
Gzip压缩
# 开启gzip
gzip on;
# 启用gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;
# gzip 压缩级别,1-10,数字越大压缩的越好,也越占用CPU时间。一般设置1和2
gzip_comp_level 2;
# 进行压缩的文件类型。javascript有多种形式。其中的值可以在 mime.types 文件中找到。
gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;
# 是否在http header中添加Vary: Accept-Encoding,建议开启
gzip_vary on;
# 禁用IE 6 gzip
gzip_disable "MSIE [1-6]\.";
# 设置缓存路径并且使用一块最大100M的共享内存,用于硬盘上的文件索引,包括文件名和请求次数,每个文件在1天内若不活跃(无请求)则从硬盘上淘汰,硬盘缓存最大10G,满了则根据LRU算法自动清除缓存。
proxy_cache_path /var/cache/nginx/cache levels=1:2 keys_zone=imgcache:100m inactive=1d max_size=10g;
参考
- https://www.nginx.com/blog/nginx-caching-guide/
- https://www.digitalocean.com/community/tutorials/how-to-optimize-nginx-configuration
- https://docs.nginx.com/nginx/admin-guide/content-cache/content-caching/