利用 Nginx 反向代理和缓存功能自建及优化 CDN 加速节点详细教程

温馨提示:
本文所述内容不具普遍性,可能因操作环境差异而与实际有所出入,故请勿照搬照抄,仅供参考。

CDN 的相关概念可以参考之前的文章《如何加快网站访问速度》,了解了什么是 CDN 后,我们知道为了提高网站相关体验,网站一般都会上 CDN,但线路质量好一点的 CDN 费用一般也不低,所以就会想着,从技术角度上看我们自己是否能够搭建 CDN?

概念

Nginx

Nginx 是一个高性能的 HTTP 和反向代理服务器,也是一个 IMAP/POP3/SMTP 服务器,Nginx 具有很多非常优越的特性:

  • 在连接高并发的情况下,具有较好的性能,支持负载均衡,实现可扩展的浏量管理;
  • 使Web服务器更灵活,更高效,更安全;
  • 能够快速灵活且可靠的传送流视频和音频内容;
  • 支持强大的 Web 加速和移动端性能的解决方案;
  • 不仅只保护数据安全,也保证网站在面对恶意流量的攻击中能正常运行,从而保护应用安全;
  • 可管理的安全的基于 HTTP API 流量的可信平台,为 API 提供安全保障和使用策略;
  • 完整的软件应用分发平台,能够取代昂贵的 ADC 硬件负载平衡器,节省成本。

智能解析

域名智能解析是指域名解析服务器根据来访者的 IP 类型,对同一域名作出相应不同解析。例如:对 IP 来自电信的访问者,将域名解析到该域名对应 IP 地址为电信的服务器上;对 IP来自联通的访问者,将域名解析到该域名对应 IP 地址为联通的服务器上;对 IP 来自香港的访问者,将域名解析到该域名对应 IP 地址为香港的服务器上;以保证访问者不因电信线路瓶颈而造成联通、香港的访客无法访问。

反向代理

反向代理(Reverse Proxy)是指用代理服务器来接收 Internet 上的连接请求,然后将请求转发给内部网络上的服务器,并将从内部服务器上得到的结果返回给 Internet 上请求连接的客户端,此时代理服务器对外就表现为一个反向代理服务器。

CDN 工作流程

000.工作流程.png

如上图所示,这是一个完整的流程图:客户端访问域名时,先向 DNS 请求域名 IP,DNS 查询到 CNAME 记录(如果没有 DNS 直接回复源服务器 IP),则进一步解析 CNAME 智能解析服务器, 智能解析收到请求根据客户端来源按规则判断并回复 CDN 节点 IP,客户端此时访问域名就连接到了回复的 CDN 节点,如果 CDN 节点没有缓存,则 CDN 就会发起连接到负载均衡器(如果没有则直接连接源服务器,一般在有多个源服务器后端时才会有负载均衡器),然后负载均衡器根据规则分流到源服务器,将内容返回给 CDN 节点,CDN 节点再返回给客户端,完成整个访问流程。

从流程上看使用 CDN 后整个过程中增加了很多路由,看起来客户端访问速度可能会变慢,但实际上,从智能解析到负载均衡,客户端几乎是没有感知的,也就是说客户端能感受到的速度就是到 CDN 节点的速度。

搭建

通过概念讲解我们知道了 CDN 工作方式,其中智能解析、负载均衡都是可以单独拿出来长篇讲解的比较复杂的内容,我们这里先以如下简化版的流程来讲解 Nginx 搭建 CDN 节点的过程,后续有时间再一起来看看智能解析、负载均衡的内容。

000.工作流程(简化).png

编译安装

CDN 最重要的功能之一就是缓存,但像博客等内容的网站会经常更新,而 Nginx 默认是不支持删除某个文件的缓存,因此我们需要额外安装缓存清除插件 ngx_cache_purge;如果准备建多个节点,我们希望能看到是由哪个节点提供服务的,这时候就可以通过插件 ngx_http_substitutions_filter_moduleheaders-more-nginx-module 来配合实现。

参考以下命令编译(基于华为云 99 元/年主机):

# 新建 Nginx 运行用户及用户组
$ groupadd www
$ useradd -s /sbin/nologin -g www www
# 下载 Nginx 源码
$ wget https://nginx.org/download/nginx-1.14.2.tar.gz
$ tar zxf nginx-1.14.2.tar.gz && cd nginx-1.14.2/src/
# 下载并编译 pcre 运行库
$ wget https://ftp.pcre.org/pub/pcre/pcre-8.42.tar.bz2
$ tar jxf pcre-8.42.tar.bz2 && cd pcre-8.42 && ./configure
$ make && make install && cd .. && rm -rf pcre-8.42*
$ echo /usr/local/lib >> /etc/ld.so.conf.d/usrlib.conf 
$ ldconfig
# 下载并解压缓存清除插件 ngx_cache_purge
$ wget https://github.com/FRiCKLE/ngx_cache_purge/archive/2.3.tar.gz && tar zxf 2.3.tar.gz && rm -rf 2.3.tar.gz
# 下载并解压字符串替换插件 ngx_http_substitutions_filter_module/
$ wget https://github.com/yaoweibin/ngx_http_substitutions_filter_module/archive/v0.6.4.tar.gz && tar zxf v0.6.4.tar.gz && rm -rf v0.6.4.tar.gz
# 下载并解压头部参数设置插件 headers-more-nginx-module
$ wget https://github.com/openresty/headers-more-nginx-module/archive/v0.33.tar.gz && tar zxf v0.33.tar.gz && rm -rf v0.33.tar.gz && cd ..
# 编译并安装
$ ./configure--user=www --group=www --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-http_v2_module --with-http_gzip_static_module --with-http_sub_module --with-stream --with-stream_ssl_module --with-http_flv_module --with-http_addition_module --with-http_realip_module --with-http_mp4_module --with-ld-opt=-Wl,-E --with-pcre --add-module=/usr/local/nginx/src/src/ngx_cache_purge-2.3 --add-module=/usr/local/nginx/src/src/ngx_http_substitutions_filter_module-0.6.4 --add-module=/usr/local/nginx/src/src/headers-more-nginx-module-0.33
$ make && make install

配置缓存

配置

主要是为了缓存静态文件,而缓存需要指定位置及大小,参见如下配置:

......
http {
......
    proxy_cache_path /usr/local/nginx/cache levels=1:2 keys_zone=mycache:20m max_size=5g inactive=1d;
    proxy_temp_path /usr/local/nginx/cache/tmp;
.......

由于缓存是写入硬盘的,硬盘的 I/O 将会对速度产生重大影响,故建议选择 I/O 好一点的主机,比如华为云学生机

说明

  • proxy_cache_path :用于缓存的本地磁盘目录是 /usr/local/nginx/cache/
  • levels:在 /usr/local/nginx/cache/ 设置了一个两级层次结构的目录。将大量的文件放置在单个目录中会导致文件访问缓慢,所以推荐使用两级目录层次结构,如果 levels 参数没有配置,则 Nginx 会将所有的文件放到同一个目录中。
  • keys_zone:设置一个共享内存区,该内存区用于存储缓存键和元数据,有些类似计时器的用途。将键的拷贝放入内存可以使 Nginx 在不检索磁盘的情况下快速决定一个请求是 HIT 还是MISS,这样大大提高了检索速度。一个 1MB 的内存空间可以存储大约 8000 个 key,那么上面配置的 20MB内存空间可以存储差不多 160000 个 key。
  • max_size:设置了缓存的上限(在上面的例子中是 5G),这是一个可选项。如果不指定具体值,那就是允许缓存不断增长,占用所有可用的磁盘空间,当缓存达到这个上线,处理器便调用 cache manager 来移除最近最少被使用的文件,这样把缓存的空间降低至这个限制之下。
  • inactive:指定了项目在不被访问的情况下能够在内存中保持的时间。在上面的例子中,如果一个文件在 1 天之内没有被请求,则缓存管理将会自动将其在内存中删除,不管该文件是否过期。该参数默认值为 10 分钟(10m)。注意,非活动内容有别于过期内容,Nginx 不会自动删除由缓存控制头部(Cache-Control)指定的过期内容,过期内容只有在 inactive 指定时间内没有被访问的情况下才会被删除,如果过期内容被访问了,那么 Nginx 就会将其从原服务器上刷新,并更新对应的 inactive 计时器。
  • proxy_temp_path:Nginx 最初会将注定写入缓存的文件先放入一个临时存储区域, use_temp_path=off 命令指示 Nginx 将在缓存这些文件时将它们写入同一个目录下,我们强烈建议你将参数设置为 off 来避免在文件系统中不必要的数据拷贝。如果没有配置 use_temp_path=off 或者没有配置则按 proxy_temp_path 设置来给定存储目录,如果 proxy_temp_path 也没配置,则直接缓存到 proxy_cache_path 目录下 。

配置代理

配置

$ sudo  echo '1.1.1.1 www.vircloud.net' >> /etc/hosts
server {
    listen 80;
    server_name www.vircloud.net;
    more_set_headers "X-Node: CN-BJ01";

    location / {
        proxy_pass https://www.vircloud.net;
        proxy_redirect off;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

        proxy_cache mycache;
        proxy_cache_key $uri$is_args$args;
    }

    location ~ /clear(/.*) {
        proxy_cache_purge mycache $1$is_args$args;
        error_page 405 =200 /clear$1;
    }
}

说明

由于 CDN 节点必须知道源站 IP,否则无法正常工作,所以我们需要先在 hosts 指定源码 IP。

这一段反代服务的配置可以说是最简单的配置了,我们来看一下各个参数的作用:

  • server_name:指定反代的域名,可以与源站不同;
  • more_set_headers:由 headers-more-nginx-module 插件提供的参数,可以自由地添加头部信息,我们这里设置了节点位置;
  • proxy_pass:要反代的域名;
  • proxy_redirect:修改从被代理服务器传来的应答头信息。比如 proxy_redirect http://localhost:8000/blog/ /; 是将源站响应的 http://localhost:8000/blog/ 替换成 /,这样就可以直接通过 https://www.vircloud.net/ 来访问 http://localhost:8000/blog/
  • proxy_set_header X-Forwarded-For:将实际访客的 IP 添加到发送给源站的请求头信息中,不然源站看到的访问 IP 都是代理服务器的 IP;
  • proxy_cache:指定缓存使用的空间,与 proxy_cache_path 配置的空间一致;
  • proxy_cache_key:指定缓存使用的 key 值,方便定位清除缓存;
  • location ~ /clear(/.*):指定访问 /clear/ 目录为清除缓存的入口;
  • proxy_cache_purge:由 ngx_cache_purge 插件提供的参数,解析要清除的缓存实际存储的位置;
  • error_page 405 =200 /clear$1:重写清除缓存的状态码。

完整配置

到这里,不出意外反代已经可以正常工作了,即已经实现了 CDN 节点的搭建。出于实际作业需求,我们可能还需要更多的配置来实现,比如源站挂了,而节点有缓存,那么我们就会希望让节点正常返回,而不是跟着源站返回错误。

下面我们就来看看 Nginx 对反代还提供了哪些参数,以实际完整配置为例:

server {
# 监听端口。
    listen  80;
    listen  443 ssl http2;
# 监听 SNI 域名。
    server_name  www.vircloud.net;
# SSL 配置。
    ssl_certificate        vircloud.net.crt;
    ssl_certificate_key    vircloud.net.key;
    ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3;
    ssl_ciphers 'TLS13-AES-256-GCM-SHA384:TLS13-CHACHA20-POLY1305-SHA256:TLS13-AES-128-GCM-SHA256:TLS13-AES-128-CCM-8-SHA256:TLS13-AES-128-CCM-SHA256:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES256-SHA384:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES128-SHA:DHE-RSA-AES256-SHA256:DHE-RSA-AES256-SHA:ECDHE-ECDSA-DES-CBC3-SHA:ECDHE-RSA-DES-CBC3-SHA:EDH-RSA-DES-CBC3-SHA:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:!DSS';
    ssl_prefer_server_ciphers on;
    ssl_session_cache shared:SSL:10m;
    ssl_session_timeout 10m;
# 获取真实 IP。
    include realip.conf;
# 反垃圾。
    include deny-ip.conf;
    include deny-bots.conf;
# SSL 跳转。
    if ($server_port !~ 443){
        rewrite ^(/.*)$ https://$host$1 permanent;
    }
# 指定错误页面,并从当前服务器取,而不向源站服务器请求。
    proxy_intercept_errors on; 
    error_page 403 404 500 502 /error.html;
    location /error.html {
        root /usr/local/nginx/html;
    }
# 添加响应头信息。
    more_set_headers "X-Node: CN-BJ01";
# 设置缓存清除入口。
    location ~ /clear(/.*) {
# 只允许特定 IP 执行清除动作。
        include allowip.conf;
        deny all;
# 缓存 key 转换。
        proxy_cache_purge mycache $1$is_args$args;
        error_page 405 =200 /clear$1;
    }
# 反代配置正式开始。
    location / {
        proxy_pass https://www.vircloud.net;
        proxy_redirect off;
# 向源站传送主机头、客户端真实 IP 等特定信息。
        proxy_set_header Host $host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        proxy_set_header X-Forwarded-Proto $scheme;
# 忽略源站响应的 Cache-Control 头信息。
        proxy_ignore_headers Cache-Control
# 与源站建立连接的超时时间,通常不要超过 75s。
        proxy_connect_timeout 60;
# 与源站通信时节点发送请求的超时时间,超时只在两次连续的写入操作之间作用, 而不是用于传输整个请求,如果源站在此时间内没有收到任何内容,则连接将关闭。
        proxy_send_timeout 60;
# 与源站通信时源站响应数据的超时时间,超时只在两次连续的读操作之间起作用,而不是用于传输整个响应,如果源站在此时间内没有传输任何内容,则连接将关闭。
        proxy_read_timeout 60;
# 开启代理缓冲区。
        proxy_buffering on;
# 响应头的缓冲区大小。
        proxy_buffer_size 128k;
# 网页内容缓冲区大小(4*256k)。
        proxy_buffers 4 256k;
# Nginx 会在没有完全读完后端响应的时候就开始向客户端传送数据,所以它会划出一部分缓冲区来专门向客户端传送数据,然后它继续从后端取数据,缓冲区满了之后就写到磁盘的临时文件中。
        proxy_busy_buffers_size 512k;
# 指定当响应内容大于 proxy_buffers 指定的缓冲区时,写入硬盘的临时文件的大小,如果超过了这个值,Nginx 将与源服务器同步的传递内容,而不再缓冲到硬盘,设置为 0 时则直接关闭硬盘缓冲。
        proxy_max_temp_file_size 512k;
# 一次访问能写入的临时文件的大小。
        proxy_temp_file_write_size 512k;
# 源站返回错误信息自动重试。
        proxy_next_upstream error timeout invalid_header http_500 http_503; 
# 若后端返回错误则返回已缓存的。
        proxy_cache_use_stale error timeout updating http_500 http_502 http_503 http_504; 
# 指定缓存使用的空间。
        proxy_cache mycache;
# 缓存支持的方法。
        proxy_cache_methods GET HEAD;
# 添加 If-Modified-Since 头信息,如果客户端的请求项已经被缓存过了,但是在缓存控制头部中定义为过期,那么 Nginx 就会在向源站 GET 请求中包含 If-Modified-Since 字段,源站只会在该文件请求头中 Last-Modified 记录的时间内被修改时才将全部文件一起发送。
        proxy_cache_revalidate on;
# 多个 MISS 只有第一个会连接服务器。
        proxy_cache_lock on; 
# 客户端设置 Pragma:no-cache 时节点应当直接请求源站,添加此配置可使 Nginx 支持该请求(默认忽略)。
        proxy_cache_bypass $http_pragma $cookie_nocache; 
# 指定对 200、301 或者 302 等有效代码缓存的时间长度,特定参数 any 表示对任何响应都缓存一定时间长度。
        proxy_cache_valid 200 24d;
        proxy_cache_valid 301 24h;
        proxy_cache_valid 302 304 403 1h;
        proxy_cache_valid any 1s;
# 指定缓存使用的 key 值,方便定位清除缓存。
        proxy_cache_key        $uri$is_args$args;
# 响应头信息添加当前请求资源的缓存状态,如命中 HIT、没有缓存 MISS、过期 UPDATING 等。
        add_header X-Cache    $upstream_cache_status;
# 指定请求多少次才开始缓存。
        proxy_cache_min_uses 1;
# 统一添加请求的资源过期时间头信息
        expires max;
    }

真实 IP

使用反代功能后,实际上对于源站来说,访客是代理服务器而不是真实的访客,Nginx 针对这种情况也给了解决方案,在源站 Nginx 配置中添加如下配置:

......
set_real_ip_from 反代服务器 IP;
real_ip_recursive on;
......

更多真实 IP 相关信息参见《使用 CDN(CloudFlare|腾讯云|加速乐等)情况下如何获取访客真实 IP ?


参考文章:

1、《关于Nginx proxy_connect_timeout 的问题
2、《Nginx反向代理中使用proxy_redirect重定向url
3、《Nginx 自建 CDN 反代加速节点
4、《Mozilla SSL Configuration Generator
5、《nginx反向代理WebSocket
6、《Module ngx_http_proxy_module
7、《Nginx 作为反向代理优化要点proxy_buffering
8、《Nginx实践篇(5)- Nginx代理服务 - 代理缓冲区、代理重新定义请求头、代理连接超时
9、《nginx 缓存与优化
10、《NGINX缓存使用官方指南


ArmxMod for Typecho
个性化、自适应、功能强大的响应式主题

推广

 继续浏览关于 加速反代nginxcdn教程 的文章

 本文最后更新于 2019/06/15 21:04:00,可能因经年累月而与现状有所差异

 引用转载请注明: VirCloud's Blog > 建站 > 利用 Nginx 反向代理和缓存功能自建及优化 CDN 加速节点详细教程

精选评论

  1. AWS CloudFront CDN 详细图文部署教程及自选优质 IP 方法 | 我的文本
  2. vpsxb
    vpsxb 回复

    Windows 10Chrome 108.0.0.0来自 浙江 的大神

    大佬您好,想申请转载到我的博客,麻烦您了,感谢。

  3. vpsxb
    vpsxb 回复

    Windows 10Chrome 108.0.0.0来自 浙江 的大神

    大佬,您好,想申请转载到我的博客,麻烦了,谢谢

    1. 欧文斯
      欧文斯 回复

      Windows 10Chrome 106.0.0.0来自 福建 的大神

      可以,注明出处