nginx学习笔记

245 阅读13分钟

nginx学习笔记

1.虚拟主机

Nginx中的server_name指令主要用于配置基于名称的虚拟主机,server_name指令在接到请求后的匹配顺序分别为:

1、精准的server_name匹配,例如:

 server {
      listen       80;
      server_name  domain.com  www.domain.com;
      ...
 }

2、通配符匹配

 server {
      listen       80;
      server_name  *.domain.com;
      ...
 }

3、正则表达式匹配

 server {
      listen       80;
      server_name  ~^(\w+).domain.com$;
      ...
 }

如果所有的server_name都不匹配,就会进入端口号相同的虚拟主机。

2.常用操作命令

Nginx 的命令在控制台中输入 nginx -h 就可以看到完整的命令,这里列举几个常用的命令:

 nginx -s reload  # 向主进程发送信号,重新加载配置文件,热重启
 nginx -s reopen  # 重启 Nginx
 nginx -s stop    # 快速关闭
 nginx -s quit    # 等待工作进程处理完成后关闭
 nginx -T         # 查看当前 Nginx 最终的配置
 nginx -t         #检查配置文件是否合法
 nginx -c 配置文件  #指定配置文件
 nginx -t -c <配置路径>    # 检查配置文件是否有问题,如果已经在配置目录,则不需要-c
 nginx -V         #查看版本和安装配置信息

3.Nginx 配置语法

就跟前面文件作用讲解的图所示,Nginx 的主配置文件是 /etc/nginx/nginx.conf,你可以使用 cat -n nginx.conf 来查看配置。

nginx.conf 结构图可以这样概括:

 main        # 全局配置,对全局生效
 ├── events  # 配置影响 Nginx 服务器或与用户的网络连接
 ├── http    # 配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置
 │   ├── upstream # 配置后端服务器具体地址,负载均衡配置不可或缺的部分
 │   ├── server   # 配置虚拟主机的相关参数,一个 http 块中可以有多个 server 块
 │   ├── server
 │   │   ├── location  # server 块可以包含多个 location 块,location 指令用于匹配 uri
 │   │   ├── location
 │   │   └── ...
 │   └── ...
 └── ...

一个 Nginx 配置文件的结构就像 nginx.conf 显示的那样,配置文件的语法规则:

  1. 配置文件由指令与指令块构成;
  2. 每条指令以 ; 分号结尾,指令与参数间以空格符号分隔;
  3. 指令块以 {} 大括号将多条指令组织在一起;
  4. include 语句允许组合多个配置文件以提升可维护性;
  5. 使用 # 符号添加注释,提高可读性;
  6. 使用 $ 符号使用变量;
  7. 部分指令的参数支持正则表达式;

3.1 典型配置

Nginx 的典型配置:

 user  nginx;                        # 运行用户,默认即是nginx,可以不进行设置
 worker_processes  1;                # Nginx 进程数,一般设置为和 CPU 核数一样
 error_log  /var/log/nginx/error.log warn;   # Nginx 的错误日志存放目录
 pid        /var/run/nginx.pid;      # Nginx 服务启动时的 pid 存放位置
 ​
 events {
     use epoll;     # 使用epoll的I/O模型(如果你不知道Nginx该使用哪种轮询方法,会自动选择一个最适合你操作系统的)
     worker_connections 1024;   # 每个进程允许最大并发数
 }
 ​
 http {   # 配置使用最频繁的部分,代理、缓存、日志定义等绝大多数功能和第三方模块的配置都在这里设置
     # 设置日志模式
     log_format  main  '$remote_addr - $remote_user [$time_local] "$request" '
                       '$status $body_bytes_sent "$http_referer" '
                       '"$http_user_agent" "$http_x_forwarded_for"';
 ​
     access_log  /var/log/nginx/access.log  main;   # Nginx访问日志存放位置
 ​
     sendfile            on;   # 开启高效传输模式
     tcp_nopush          on;   # 减少网络报文段的数量
     tcp_nodelay         on;
     keepalive_timeout   65;   # 保持连接的时间,也叫超时时间,单位秒
     types_hash_max_size 2048;
 ​
     include             /etc/nginx/mime.types;      # 文件扩展名与类型映射表
     default_type        application/octet-stream;   # 默认文件类型
 ​
     include /etc/nginx/conf.d/*.conf;   # 加载子配置项
     
     server {
         listen       80;       # 配置监听的端口
         server_name  localhost;    # 配置的域名
         
         location / {
             root   /usr/share/nginx/html;  # 网站根目录
             index  index.html index.htm;   # 默认首页文件
             deny 172.168.22.11;   # 禁止访问的ip地址,可以为all
             allow 172.168.33.44; # 允许访问的ip地址,可以为all
         }
         
         error_page 500 502 503 504 /50x.html;  # 默认50x对应的访问页面
         error_page 400 404 error.html;   # 同上
     }
 }

server 块可以包含多个 location 块,location 指令用于匹配 uri,语法:

 location [ = | ~ | ~* | ^~] uri {
     ...
 }
 复制代码

指令后面:

  1. = 精确匹配路径,只有完全匹配上才能生效,如果匹配成功,不再进行后续的查找;
  2. ^~ 对URL路径进行前缀匹配,并且在正则之前;匹配后继续尝试匹配,匹配/ui最长为准
  3. ~ 表示区分大小写的正则匹配,匹配后不会再继续匹配,多个正则匹配与配置文件中的顺序有关;
  4. ~* 表示不区分大小写的正则匹配,匹配后不会再继续匹配;多个正则匹配与配置文件中的顺序有关。
  5. /uri 不带任何修饰符,也表示前缀匹配,但是优先级在正则匹配之后,匹配后继续尝试匹配,以匹配/uri最长为准
  6. / 通用匹配,如果其他都没匹配上,则命中此配置。

如果 uri 包含正则表达式,则必须要有 ~~* 标志。

3.2 全局变量

Nginx 有一些常用的全局变量,你可以在配置的任何位置使用它们,如下表:

全局变量名功能
$host请求头中的 Host,不包含端口
$http_host请求头中的Host 包含端口号 www.ldk.com:8080
$request_uri请求路径 和$uri类似 $request_uri带参数,$uri不带参数
$request_method客户端请求类型,如 GETPOST
$request完整的HTTP请求行,包括HTTP方法、URI和协议版本
$statusHTTP响应状态码 200表示成功
$is_args如果请求中有参数,值为?,否则为空字符串。
$args请求中的参数 如:a=10&b=20
$arg_PARAMETERGET 请求中变量名 PARAMETER 参数的值,例如:$http_user_agent(Uaer-Agent 值), $http_referer...
$content_length请求头中的 Content-length 字段
$http_user_agent客户端agent信息
$http_cookie客户端cookie信息
$remote_addr客户端的IP地址
$remote_port客户端的端口
$remote_user远程用户
$http_user_agent客户端agent信息
$server_protocol请求使用的协议,如 HTTP/1.0HTTP/1.1
$server_addr服务器地址
$server_name服务器名称
$server_port服务器的端口号
$schemeHTTP 方法(如http,https)
$time_local访问时间和日期,以服务器的本地时区为准
$body_bytes_sent响应体字节数
$http_referer请求头中得referer
$http_user_agent客户端的User-Agent标头
$scheme请求模式,http或https
$pid进程id
$proxy_host代理服务器主机,如localhost:8080
$http_x_forwarded_for通过代理服务器传递的客户端IP地址

$proxy_add_x_forwarded_for客户端发送请求,有多个代理,会把客户端ip和代理ip拼接在一起,用逗号分隔。例如请求路径:clinet1-->proxy1-->proxy2-->proxy3 那么$proxy_add_x_forwarded_for值为:client1,proxy1,proxy2,proxy3

3.3Rewrite相关指令

Rewrite命令

语法:

 rewrite <regex> <replacement> [flag];
 ​
 regex 正则表达式
 replacement 替换的内容
 flag 标记

flag标记说明:

标记说明
last会再次进入server块,重试location匹配,地址栏不变
break本条规则匹配完成即终止,不再匹配后面的任务规则 地址栏不变
redirect返回302临时重定向,浏览器地址会显示跳转后的URL地址,爬虫不会更新url
permanent返回301永久重定向,浏览器地址会显示跳转后的URL地址,爬虫更新url
set指令

该指令用来设置一个新的变量。

语法set $variable value;
默认值
位置server、location、if

variable:变量的名称,该变量名称要用"$"作为变量的第一个字符,且不要与Nginx服务器预设的全局变量同名。

value:变量的值,可以是字符串、其他变量或者变量的组合等。

 set $cusArgs $args;
 set $myname 'ldk'; 
if指令

该指令用来支持条件判断,并根据条件判断结果选择不同的Nginx配置。if和后面括号之间需要加空格

语法if (condition){...}
默认值
位置server、location

condition为判定条件,可以支持以下写法:

  1. 变量名。如果变量名对应的值为空字符串或"0",if都判断为false,其他条件为true。
#if后面需要加空格
 if ($param){
     
 }
 if ($is_args){
    set $a '你好中国'; 
}
  1. 使用"="和"!="比较变量和字符串是否相等,满足条件为true,不满足为false
#if后面需要加空格
 if ($request_method = POST){
     return 405;
 }

注意:此处和Java不太一样的地方是字符串不需要添加引号,并且等号和不等号前后到需要加空格。

  1. 使用正则表达式对变量进行匹配,匹配成功返回true,否则返回false。变量与正则表达式之间使用"","","!","!"来连接。

    "~"代表匹配正则表达式过程中区分大小写,

    "~*"代表匹配正则表达式过程中不区分大小写

    "!"和"!*"刚好和上面取相反值,如果匹配上返回false,匹配不上返回true

 if ($http_user_agent ~ MSIE){
     #$http_user_agent的值中是否包含MSIE字符串,如果包含返回true
 }

注意:正则表达式字符串一般不需要加引号,但是如果字符串中包含"}"或者是";"等字符时,就需要把引号加上。

  1. 判断请求的文件是否存在使用"-f"和"!-f",
 if (-f $request_filename){
     #判断请求的文件是否存在
 }
 if (!-f $request_filename){
     #判断请求的文件是否不存在
 }

5. 判断请求的目录是否存在使用"-d"和"!-d" 6. 判断请求的目录或者文件是否存在使用"-e"和"!-e" 7. 判断请求的文件是否可执行使用"-x"和"!-x"

break指令

该指令用于中断当前相同作用域中的其他Nginx配置。与该指令处于同一作用域的Nginx配置中,位于它前面的指令配置生效,位于后面的指令配置无效。并且break还有另外一个功能就是终止当前的匹配并把当前的URI在本location进行重定向访问处理。

语法break;
默认值
位置server、location、if

例子:

 location /testbreak{
     default_type text/plain;
     set $username TOM;
     if ($args){
         Set $username JERRY;
         break;
         set $username ROSE;
     }
     add_header username $username;
     return 200 $username;
 }
return指令

该指令用于完成对请求的处理,直接向客户端返回。在return后的所有Nginx配置都是无效的。

语法return code [text]; return code URL; return URL;
默认值
位置server、location、if

code:为返回给客户端的HTTP状态代理。可以返回的状态代码为0~999的任意HTTP状态代理

text:为返回给客户端的响应体内容,支持变量的使用

URL:为返回给客户端的URL地址

 location /testreturn {
 ​
     return 200 success;
 }
 ​
 location /testreturn {
 ​
     return https://www.baidu.com; // 302重定向到百度
 }
 ​
 location /testreturn {
     return 302 https://www.baidu.com;
 }
 ​
 location /testreturn {
     return 302 www.baidu.com;//不允许这么写
 }

4. 配置反向代理

反向代理是工作中最常用的服务器功能,经常被用来解决跨域问题,下面简单介绍一下如何实现反向代理。

首先进入 Nginx 的主配置文件:

 vim /etc/nginx/nginx.conf

为了看起来方便,把行号显示出来 :set nu (个人习惯),然后我们去 http 模块的 server 块中的 location /,增加一行将默认网址重定向到最大学习网站 Bilibili 的 proxy_pass 配置 🤓 :

image-20200311153131642

改完保存退出,nginx -s reload 重新加载,进入默认网址,那么现在就直接跳转到 B 站了,实现了一个简单的代理。

实际使用中,可以将请求转发到本机另一个服务器上,也可以根据访问的路径跳转到不同端口的服务中。

比如我们监听 9001 端口,然后把访问不同路径的请求进行反向代理:

  1. 把访问 http://127.0.0.1:9001/edu 的请求转发到 http://127.0.0.1:8080
  2. 把访问 http://127.0.0.1:9001/vod 的请求转发到 http://127.0.0.1:8081

这种要怎么配置呢,首先同样打开主配置文件,然后在 http 模块下增加一个 server 块:

 upstream backend{
     server 172.168.254.4:8080 weight=8;
     server 172.168.254.5:8080 weight=2 backup; #backup 表示备份,如果上面的server没有挂,不会接收请求,如果上面的server 宕机,开始接收请求。
     server 172.168.254.6:8080  down; #down表示已下线
 }
 ​
 server {
   listen 9001;
   server_name *.sherlocked93.club;
 ​
   location ~ /edu/ {
     proxy_pass http://127.0.0.1:8080;
   }
   
   location ~ /vod/ {
     proxy_pass http://127.0.0.1:8081;
   }
     
   location ~ /abc/ {
     proxy_pass http://backend;
   }  
 }

反向代理还有一些其他的指令,可以了解一下:

  1. proxy_set_header:在将客户端请求发送给后端服务器之前,更改来自客户端的请求头信息。
  2. proxy_connect_timeout:配置Nginx与后端代理服务器尝试建立连接的超时时间。
  3. proxy_read_timeout:配置Nginx向后端服务器组发出read请求后,等待相应的超时时间。
  4. proxy_send_timeout:配置Nginx向后端服务器组发出write请求后,等待相应的超时时间。
  5. proxy_redirect:用于修改后端服务器返回的响应头中的Location和Refresh。

4.1反向代理路径后加斜杠问题

我浏览器中访问的地址:http://192.168.10.1/proxy/test.html

第一种(末尾加斜杠,proxy_pass中不包含路径)

location  /proxy/ {
	proxy_pass http://127.0.0.1:81/; #注意:这里的路径加了一个斜杠
}

生成代理后路径:http://127.0.0.1:81/test.html ,proxy_pass+请求url匹配的location路径后的内容。

第二种(末尾不加斜杠,proxy_pass不包含路径)

location  /proxy/ {
	proxy_pass http://127.0.0.1:81; #注意:这里的代理路径没有斜杠
}

生成代理后路径:http://127.0.0.1:81/proxy/test.html ,(proxy_pass替换请求url的ip和端口)

第三种(末尾加斜杠,proxy_pass包含路径):

location  /proxy/ {
	proxy_pass http://127.0.0.1:81/abc/; #注意:这里的代理路径不仅有ip和端口,还有uri路径,并且以斜杠结尾。
}

结论:会被代理到http://127.0.0.1:81/abc/test.html (proxy_pass+请求url匹配的location路径后的内容)

第四种(末尾不加斜杠,url包含路径):

location  /proxy/ {
	proxy_pass http://127.0.0.1:81/abc; #注意:这里的代理路径不仅有ip和端口,还有uri路径,并且结尾没有斜杠。
}

结论:会被代理到http://127.0.0.1:81/abctest.html (proxy_pass+请求url匹配的location路径后的内容)

5. 跨域 CORS 配置

关于简单请求、非简单请求、跨域的概念,前面已经介绍过了,还不了解的可以看看前面的讲解。现在前后端分离的项目一统天下,经常本地起了前端服务,需要访问不同的后端地址,不可避免遇到跨域问题。

image-20200427220536208

要解决跨域问题,我们来制造一个跨域问题。首先和前面设置二级域名的方式一样,先设置好 fe.sherlocked93.clubbe.sherlocked93.club 二级域名,都指向本云服务器地址,虽然对应 IP 是一样的,但是在 fe.sherlocked93.club 域名发出的请求访问 be.sherlocked93.club 域名的请求还是跨域了,因为访问的 host 不一致(如果不知道啥原因参见前面跨域的内容)。

5.1 使用反向代理解决跨域

在前端服务地址为 fe.sherlocked93.club 的页面请求 be.sherlocked93.club 的后端服务导致的跨域,可以这样配置:

 server {
   listen 9001;
   server_name fe.sherlocked93.club;
 ​
   location / {
     proxy_pass be.sherlocked93.club;
   }
 }

这样就将对前一个域名 fe.sherlocked93.club 的请求全都代理到了 be.sherlocked93.club,前端的请求都被我们用服务器代理到了后端地址下,绕过了跨域。

这里对静态文件的请求和后端服务的请求都以 fe.sherlocked93.club 开始,不易区分,所以为了实现对后端服务请求的统一转发,通常我们会约定对后端服务的请求加上 /apis/ 前缀或者其他的 path 来和对静态资源的请求加以区分,此时我们可以这样配置:

 # 请求跨域,约定代理后端服务请求path以/apis/开头
 location ^~/apis/ {
     # 这里重写了请求,将正则匹配中的第一个分组的path拼接到真正的请求后面,并用break停止后续匹配
     rewrite ^/apis/(.*)$ /$1 break;
     proxy_pass be.sherlocked93.club;
   
     # 两个域名之间cookie的传递与回写
     proxy_cookie_domain be.sherlocked93.club fe.sherlocked93.club;
 }

这样,静态资源我们使用 fe.sherlocked93.club/xx.html,动态资源我们使用 fe.sherlocked93.club/apis/getAwo,浏览器页面看起来仍然访问的前端服务器,绕过了浏览器的同源策略,毕竟我们看起来并没有跨域。

也可以统一一点,直接把前后端服务器地址直接都转发到另一个 server.sherlocked93.club,只通过在后面添加的 path 来区分请求的是静态资源还是后端服务,看需求了。

5.2 配置 header 解决跨域

 server {
   listen       80;
   server_name  be.sherlocked93.club;
   
     add_header 'Access-Control-Allow-Origin' $http_origin;   # 全局变量获得当前请求origin,带cookie的请求不支持*
     add_header 'Access-Control-Allow-Credentials' 'true';    # 为 true 可带上 cookie
     add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';  # 允许请求方法
     add_header 'Access-Control-Allow-Headers' $http_access_control_request_headers;  # 允许请求的 header,可以为 *
     add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     
     if ($request_method = 'OPTIONS') {
         add_header 'Access-Control-Max-Age' 1728000;   # OPTIONS 请求的有效期,在有效期内不用发出另一条预检请求
         add_header 'Content-Type' 'text/plain; charset=utf-8';
         add_header 'Content-Length' 0;
     
         return 204;                  # 200 也可以
     }
   
     location / {
         root  /usr/share/nginx/html/be;
         index index.html;
     }
 }

6.常用指令

6.1try_files指令

try_files指令可以按顺序检查文件是否存在,并返回第一个找到的文件, 如果未找到任何文件,则会调用最后一个参数进行内部重定向。

示例一:

location /whsir/ {
    try_files $uri /whsir/default.gif;
}

说明:

1、访问www.example.com/whsir/123/321(文件不存在)时,此时看到的是default.gif图片,URL地址不变

2、访问www.example.com/whsir/123.png(文件存在)时,此时看到的是123.png图片,URL地址不变

总结:当images目录下文件不存在时,默认返回default.gif

示例二:

location /whsir/ {
    try_files $uri =403;
}

说明:

1、访问www.example.com/whsir/123.html(文件存在)时,此时看到的是123.html内容,URL地址不变

2、访问www.example.com/whsir/21.html(文件不存在)时,此时看到的是403状态,URL地址不变

总结:和示例一一样,只是将默认图片换成了403状态

示例三:

location /whsir/ {
    try_files $uri @ab;
}
location @ab {
    rewrite ^/(.*)$ https://blog.whsir.com;
}

说明:

1、访问www.example.com/whsir/123.html(文件存在)时,此时看到的是123.html内容,URL地址不变

2、访问www.example.com/whsir/21.html(文件不存在)时,此时跳转到吴昊博客,URL地址改变

总结:当文件不存在时,会去查找@ab值,此时在location中定义@ab值跳转到吴昊博客

示例四:

try_files $uri @pro;
location @pro {
    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_pass https://blog.whsir.com;
}

说明:

1、访问www.example.com/123.html(文件存在)时,此时看到的是123.html内容,URL地址不变

2、访问www.example.com/post-3647.html(文件不存在)时,此时看到的是吴昊博客的内容,URL地址不变

示例五:

location /abc {
    root /usr/share/nginx/html;
    try_files $uri $uri/ /index.html;
}

说明: 在目录/usr/share/nginx/html/abc下有index.html文件

1、访问http://192.168.254.4:8001/abc/ 会显示/abc/index.html文件

2、访问http://192.168.254.4:8001/abc/def 会重定向到/usr/share/nginx/html/index.html文件

7.资源压缩

建立在动静分离的基础之上,如果一个静态资源的Size越小,那么自然传输速度会更快,同时也会更节省带宽,因此我们在部署项目时,也可以通过Nginx对于静态资源实现压缩传输,一方面可以节省带宽资源,第二方面也可以加快响应速度并提升系统整体吞吐。

Nginx也提供了三个支持资源压缩的模块ngx_http_gzip_module、ngx_http_gzip_static_module、ngx_http_gunzip_module,其中ngx_http_gzip_module属于内置模块,代表着可以直接使用该模块下的一些压缩指令,后续的资源压缩操作都基于该模块,先来看看压缩配置的一些参数/指令:

参数项释义参数值
gzip开启或关闭压缩机制on/off;
gzip_types根据文件类型选择性开启压缩机制image/png、text/css...
gzip_comp_level用于设置压缩级别,级别越高越耗时1~9(越高压缩效果越好)
gzip_vary设置是否携带Vary:Accept-Encoding头域的响应头部on/off;
gzip_buffers设置处理压缩请求的缓冲区数量和大小数量 大小,如16 8k;
gzip_disable针对不同客户端的请求来设置是否开启压缩.*Chrome.*;
gzip_http_version指定压缩响应所需要的最低HTTP请求版本1.1;
gzip_min_length设置触发压缩的文件最低大小512k;
gzip_proxied对于后端服务器的响应结果是否开启压缩off、expired、no-cache...

了解了Nginx中的基本压缩配置后,接下来可以在Nginx中简单配置一下:

http{
    # 开启压缩机制
    gzip on;
    # 指定会被压缩的文件类型(也可自己配置其他类型)
    gzip_types text/plain application/javascript text/css application/xml text/javascript image/jpeg image/gif image/png;
    # 设置压缩级别,越高资源消耗越大,但压缩效果越好
    gzip_comp_level 5;
    # 在头部中添加Vary: Accept-Encoding(建议开启)
    gzip_vary on;
    # 处理压缩请求的缓冲区数量和大小
    gzip_buffers 16 8k;
    # 对于不支持压缩功能的客户端请求不开启压缩机制
    gzip_disable "MSIE [1-6]."; # 低版本的IE浏览器不支持压缩
    # 设置压缩响应所支持的HTTP最低版本
    gzip_http_version 1.1;
    # 设置触发压缩的最小阈值
    gzip_min_length 2k;
    # 关闭对后端服务器的响应结果进行压缩
    gzip_proxied off;
}

在上述的压缩配置中,最后一个gzip_proxied选项,可以根据系统的实际情况决定,总共存在多种选项:

  • off:关闭Nginx对后台服务器的响应结果进行压缩。
  • expired:如果响应头中包含Expires信息,则开启压缩。
  • no-cache:如果响应头中包含Cache-Control:no-cache信息,则开启压缩。
  • no-store:如果响应头中包含Cache-Control:no-store信息,则开启压缩。
  • private:如果响应头中包含Cache-Control:private信息,则开启压缩。
  • no_last_modified:如果响应头中不包含Last-Modified信息,则开启压缩。
  • no_etag:如果响应头中不包含ETag信息,则开启压缩。
  • auth:如果响应头中包含Authorization信息,则开启压缩。
  • any:无条件对后端的响应结果开启压缩机制。

注意点: ①对于图片、视频类型的数据,会默认开启压缩机制,因此一般无需再次开启压缩。 ②对于.js文件而言,需要指定压缩类型为application/javascript,而并非text/javascript、application/x-javascript

响应头:

Content-Encoding: gzip
Vary: Accept-Encoding

8.缓存

http{
    # 设置缓存的目录,并且内存中缓存区名为hot_cache,大小为128m,
    # 三天未被访问过的缓存自动清除,磁盘中缓存的最大容量为2GB。
    proxy_cache_path /soft/nginx/cache levels=1:2 keys_zone=hot_cache:128m inactive=3d max_size=2g;
    
    server{
        location / {
            # 使用名为hot_cache的缓存空间
            proxy_cache hot_cache;
            # 对于200、206、304、301、302状态码的数据缓存1天
            proxy_cache_valid 200 206 304 301 302 1d;
            # 对于其他状态的数据缓存30分钟
            proxy_cache_valid any 30m;
            # 定义生成缓存键的规则(请求的url+参数作为key)
            proxy_cache_key $host$uri$is_args$args;
            # 资源至少被重复访问三次后再加入缓存
            proxy_cache_min_uses 3;
            # 出现重复请求时,只让一个去后端读数据,其他的从缓存中读取
            proxy_cache_lock on;
            # 上面的锁超时时间为3s,超过3s未获取数据,其他请求直接去后端
            proxy_cache_lock_timeout 3s;
            # 对于请求参数或cookie中声明了不缓存的数据,不再加入缓存
            proxy_no_cache $cookie_nocache $arg_nocache $arg_comment;
            # 在响应头中添加一个缓存是否命中的状态(便于调试)
            add_header Cache-status $upstream_cache_status;
        }
    }
}

proxy_cache_path:代理缓存的路径。

  • 语法: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];

  • 是的,你没有看错,就是这么长....,解释一下每个参数项的含义:

    • path:缓存的路径地址。

    • levels:缓存存储的层次结构,最多允许三层目录。

      levels=1:2   缓存空间有两层目录,第一次是1个字母,第二次是2个字母
      举例说明:
      proxy_cache_key通过MD5加密以后的值为 43c8233266edce38c2c9af0694e2107d
      levels=1:2   最终的存储路径为/usr/local/proxy_cache/d/07
      levels=2:1:2 最终的存储路径为/usr/local/proxy_cache/7d/0/21
      levels=2:2:2 最终的存储路径为/usr/local/proxy_cache/7d/10/e2
      
    • use_temp_path:是否使用临时目录。

    • keys_zone:指定一个共享内存空间来存储热点Key(1M可存储8000Key)。

    • inactive:设置缓存多长时间未被访问后删除(默认是十分钟)。

    • max_size:允许缓存的最大存储空间,超出后会基于LRU算法移除缓存,Nginx会创建一个Cache manager的进程移除数据,也可以通过purge方式。

    • manager_filesmanager进程每次移除缓存文件数量的上限。

    • manager_sleepmanager进程每次移除缓存文件的时间上限。

    • manager_thresholdmanager进程每次移除缓存后的间隔时间。

    • loader_files:重启Nginx载入缓存时,每次加载的个数,默认100

    • loader_sleep:每次载入时,允许的最大时间上限,默认200ms

    • loader_threshold:一次载入后,停顿的时间间隔,默认50ms

    • purger:是否开启purge方式移除数据。

    • purger_files:每次移除缓存文件时的数量。

    • purger_sleep:每次移除时,允许消耗的最大时间。

    • purger_threshold:每次移除完成后,停顿的间隔时间。

proxy_cache:开启或关闭代理缓存,开启时需要指定一个共享内存区域。

  • 语法:

    proxy_cache zone | off;
    
    • zone为内存区域的名称,即上面中keys_zone设置的名称。

proxy_cache_key:定义如何生成缓存的键。

  • 语法:

    proxy_cache_key string;
    
    • string为生成Key的规则,如$scheme$proxy_host$request_uri

proxy_cache_valid:缓存生效的状态码与过期时间。

  • 语法:

    proxy_cache_valid [code ...] time;
    
    • code为状态码,time为有效时间,可以根据状态码设置不同的缓存时间。
    • 例如:proxy_cache_valid 200 302 30m;

proxy_cache_min_uses:设置资源被请求多少次后被缓存。

  • 语法:

    proxy_cache_min_uses number;
    
    • number为次数,默认为1

proxy_cache_use_stale:当后端出现异常时,是否允许Nginx返回缓存作为响应。

  • 语法:

    proxy_cache_use_stale error;
    
    • error为错误类型,可配置timeout|invalid_header|updating|http_500...

proxy_cache_lock:对于相同的请求,是否开启锁机制,只允许一个请求发往后端。

  • 语法:proxy_cache_lock on | off;

proxy_cache_lock_timeout:配置锁超时机制,超出规定时间后会释放请求。

  • proxy_cache_lock_timeout time;

proxy_cache_methods:设置对于那些HTTP方法开启缓存。

  • 语法:

    proxy_cache_methods method;
    
    • method为请求方法类型,如GET、HEAD等。

proxy_no_cache:定义不存储缓存的条件,符合时不会保存。

  • 语法:

    proxy_no_cache string...;
    
    • string为条件,例如$cookie_nocache $arg_nocache $arg_comment;

      $cookie_nocache
      指的是当前请求的cookie中键的名称为nocache对应的值
      $arg_nocache$arg_comment
      指的是当前请求的参数中属性名为nocache和comment对应的属性值
      

proxy_cache_bypass:定义不读取缓存的条件,符合时不会从缓存中读取。

  • 语法:

    proxy_cache_bypass string...;
    
    • 和上面proxy_no_cache的配置方法类似。

add_header:往响应头中添加字段信息。

  • 语法:add_header fieldName fieldValue;

$upstream_cache_status:记录了缓存是否命中的信息,存在多种情况:

  • MISS:请求未命中缓存。
  • HIT:请求命中缓存。
  • EXPIRED:请求命中缓存但缓存已过期。
  • STALE:请求命中了陈旧缓存。
  • REVALIDDATEDNginx验证陈旧缓存依然有效。
  • UPDATING:命中的缓存内容陈旧,但正在更新缓存。
  • BYPASS:响应结果是从原始服务器获取的。
  • PS:这个和之前的不同,之前的都是参数项,这个是一个Nginx内置变量。

8.1缓存清理

当缓存过多时,如果不及时清理会导致磁盘空间被“吃光”,因此我们需要一套完善的缓存清理机制去删除缓存,在之前的proxy_cache_path参数中有purger相关的选项,开启后可以帮我们自动清理缓存,但遗憾的是:purger系列参数只有商业版的NginxPlus才能使用,因此需要付费才可使用。

不过天无绝人之路,我们可以通过强大的第三方模块ngx_cache_purge来替代,先来安装一下该插件: ①首先去到Nginx的安装目录下,创建一个cache_purge目录:

[root@localhost]# mkdir cache_purge && cd cache_purge

②通过wget指令从github上拉取安装包的压缩文件并解压:

[root@localhost]# wget https://github.com/FRiCKLE/ngx_cache_purge/archive/2.3.tar.gz
[root@localhost]# tar -xvzf 2.3.tar.gz

③再次去到之前Nginx的解压目录下:

[root@localhost]# cd /soft/nginx/nginx1.21.6

④重新构建一次Nginx,通过--add-module的指令添加刚刚的第三方模块:

[root@localhost]# ./configure --prefix=/soft/nginx/ --add-module=/soft/nginx/cache_purge/ngx_cache_purge-2.3/

⑤重新根据刚刚构建的Nginx,再次编译一下,但切记不要make install

[root@localhost]# make

⑥删除之前Nginx的启动文件,不放心的也可以移动到其他位置:

[root@localhost]# rm -rf /soft/nginx/sbin/nginx

⑦从生成的objs目录中,重新复制一个Nginx的启动文件到原来的位置:

[root@localhost]# cp objs/nginx /soft/nginx/sbin/nginx

8.使用make进行升级

make upgrade

至此,第三方缓存清除模块ngx_cache_purge就安装完成了,接下来稍微修改一下nginx.conf配置,再添加一条location规则:

location ~ /purge(/.*) {
  # 配置可以执行清除操作的IP(线上可以配置成内网机器)
  # allow 127.0.0.1; # 代表本机
  allow all; # 代表允许任意IP清除缓存
  proxy_cache_purge $host$1$is_args$args;
}

然后再重启Nginx,接下来即可通过http://xxx/purge/xx的方式清除缓存。

9.Nginx实现IP黑白名单

有时候往往有些需求,可能某些接口只能开放给对应的合作商,或者购买/接入API的合作伙伴,那么此时就需要实现类似于IP白名单的功能。而有时候有些恶意攻击者或爬虫程序,被识别后需要禁止其再次访问网站,因此也需要实现IP黑名单。那么这些功能无需交由后端实现,可直接在Nginx中处理。

Nginx做黑白名单机制,主要是通过allow、deny配置项来实现:

allow xxx.xxx.xxx.xxx; # 允许指定的IP访问,可以用于实现白名单。
deny xxx.xxx.xxx.xxx; # 禁止指定的IP访问,可以用于实现黑名单。

要同时屏蔽/开放多个IP访问时,如果所有IP全部写在nginx.conf文件中定然是不显示的,这种方式比较冗余,那么可以新建两个文件BlocksIP.conf、WhiteIP.conf

# --------黑名单:BlocksIP.conf---------
deny 192.177.12.222; # 屏蔽192.177.12.222访问
deny 192.177.44.201; # 屏蔽192.177.44.201访问
deny 127.0.0.0/8; # 屏蔽127.0.0.1到127.255.255.254网段中的所有IP访问# --------白名单:WhiteIP.conf---------
allow 192.177.12.222; # 允许192.177.12.222访问
allow 192.177.44.201; # 允许192.177.44.201访问
allow 127.45.0.0/16; # 允许127.45.0.1到127.45.255.254网段中的所有IP访问
deny all; # 除开上述IP外,其他IP全部禁止访问

分别将要禁止/开放的IP添加到对应的文件后,可以再将这两个文件在nginx.conf中导入:

http{
    # 屏蔽该文件中的所有IP
    include /soft/nginx/IP/BlocksIP.conf; 
 server{
    location xxx {
        # 某一系列接口只开放给白名单中的IP
        include /soft/nginx/IP/blockip.conf; 
    }
 }
}

对于文件具体在哪儿导入,这个也并非随意的,如果要整站屏蔽/开放就在http中导入,如果只需要一个域名下屏蔽/开放就在sever中导入,如果只需要针对于某一系列接口屏蔽/开放IP,那么就在location中导入。

当然,上述只是最简单的IP黑/白名单实现方式,同时也可以通过ngx_http_geo_module、ngx_http_geo_module第三方库去实现(这种方式可以按地区、国家进行屏蔽,并且提供了IP库)。