Nginx用作 内网CDN / edge cache 服务

什么是CDN?

首先要明白什么是CDN?

CDN英文全称Content Delivery Network,中文翻译即为内容分发网络。它是建立并覆盖在承载网之上,由不同区域的服务器组成的分布式网络。将源站资源缓存到全国各地的边缘服务器,供用户就近获取,降低源站压力。

CDN应用广泛,支持多种行业、多种场景内容加速,例如:图片小文件、大文件下载、视音频点播、直播流媒体、全站加速、安全加速。

加速原理如下

以下内容来着阿里云

假设您的加速域名为www.aliyundoc.com,接入CDN开始加速服务后,当终端用户在北京发起HTTP请求时,处理流程如下图所示。原理

  1. 当终端用户向www.aliyundoc.com下的指定资源发起请求时,首先向Local DNS(本地DNS)发起请求域名www.aliyundoc.com对应的IP。
  2. Local DNS检查缓存中是否有www.aliyundoc.com的IP地址记录。如果有,则直接返回给终端用户;如果没有,则向网站授权DNS请求域名www.aliyundoc.com的解析记录。
  3. 当网站授权DNS解析www.aliyundoc.com后,返回域名的CNAME www.aliyundoc.com.example.com
  4. Local DNS向阿里云CDN的DNS调度系统请求域名www.aliyundoc.com.example.com的解析记录,阿里云CDN的DNS调度系统将为其分配最佳节点IP地址。
  5. Local DNS获取阿里云CDN的DNS调度系统返回的最佳节点IP地址。
  6. Local DNS将最佳节点IP地址返回给用户,用户获取到最佳节点IP地址。
  7. 用户向最佳节点IP地址发起对该资源的访问请求。
    • 如果该最佳节点已缓存该资源,则会将请求的资源直接返回给用户(步骤8),此时请求结束。
    • 如果该最佳节点未缓存该资源或者缓存的资源已经失效,则节点将会向源站发起对该资源的请求。获取源站资源后结合用户自定义配置的缓存策略,将资源缓存到CDN节点并返回给用户(步骤8),此时请求结束。

从这个例子可以了解到:

  1. CDN的加速资源是跟域名绑定的。
  2. 通过域名访问资源,首先是通过DNS查找离用户最近的CDN节点(边缘服务器)的IP
  3. 通过IP访问实际资源时,如果CDN上并没有缓存资源,则会到源站请求资源,并缓存到CDN节点上,这样,用户下一次访问时,该CDN节点就会有对应资源的缓存了。

简单讲,CDN就是通过将站点内容发布至遍布全球的海量加速节点,使其用户可就近获取所需内容。

CDN主要解决什么问题?

  • 物理距离远,多次网络转发,延时高不稳定;
  • 所在运营商不同,需运营商之间转发绕行;
  • 高并发访问时,网络带宽处理能力有限,海量请求时,响应速度与可用性降低。
  • 高并发下载或者下载突增场景下对源站性能要求非常高,且源站的带宽成本也较高。

为什么需要 内网CDN/edge cache?

我来说一种场景,当大型公司或者学校等大型单位在观看直播时,其总的出口带宽是有限的,当有很多人同时在看直播时会把总出口带宽拉满。

优化前

通过中间加个cache层优化后

Nginx配置

我们使用Nginx作为edge cache,配置NGINX的缓存策略就可以,不需要额外插件,NGINX本身就支持:

Edge Cache服务配置

启用基本缓存只需要两个指令:proxy_cache_pathproxy_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 $upstream_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/c86156f7dcfecf44876ca30d1bac7feb
  • keys_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而言比较有用,对于客户端也有一些价值。

配置AWS cloudfront反向代理

    proxy_cache_path  /spare levels=1:2 keys_zone=nginx_cache:10m max_size=1g inactive=10m use_temp_path=off;

    server {
        listen       80;
        server_name  localhost;
        proxy_cache_valid any 5m;

        location / {
            proxy_pass https://xxx.cloudfront.net;
            proxy_set_header Host d23kaes948ag5n.cloudfront.net;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header REMOTE-HOST $remote_addr;
            # 重点配置这里 否则反向代理失败
            proxy_ssl_name xxx.cloudfront.net;
            proxy_ssl_server_name on;
            # 开启缓存
            proxy_cache nginx_cache;
            proxy_cache_key $uri;
            proxy_read_timeout 10s;
            proxy_cache_lock on;
            # 可以在响应header中查看是否名字缓存
            add_header X-Proxy-Laker-Cache $upstream_cache_status;
        }

简单静态文件缓存服务配置

对于站点中不经常修改的静态内容(如图片,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缓存规则由响应首部字段进行控制,其中的关键字段有ExpiresCache-ControlLast-ModifiedEtag 四个字段,ExpiresCache-Control用来确定确定缓存的存储时间,Last-ModifiedEtag则用来确定缓存是否要被更新,我们简单来看一下区别。

  • 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-SinceIf-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/
  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

lakernote

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值