Nginx 核心配置文件

Nginx 核心配置文件

从前面的内容学习中,我们知道 Nginx 的核心配置文件默认是放在 /usr/local/nginx/conf/nginx.conf,本次我们就来学习下 nginx.conf 的内容和基本配置方法。


配置文件内容

读取 Nginx 自带的 Nginx 配置文件,配置文件内容很多,我们先将其中的注释部分【学习一个技术点就是在 Nginx 的配置文件中可以使用 # 来注释】删除掉后,就剩下如下内容:

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
worker_processes  1;   # 使用指令 1 

events { # 这是 events 块
worker_connections 1024;
}

http { # 这是 http 块
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;

server {
listen 80; # 监听 80 端口
server_name localhost; # 监听请求过来的 IP
location / { # 请求的地址是 /,则进入这个配置,访问 idnex.html
root html; # 进入 html 目录找到访问的页面
index index.html index.htm;
}
# 如果访问的页面是 500 502 503 504,则发送 /50x.html 请求
error_page 500 502 503 504 /50x.html;
location = /50x.html { # 如果匹配上 /50x.html 请求
root html; # 则进入 html 目录找到 /50x.html
}
}
}

对上面文件内容的解释,一一对应比较解释:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
指令名	指令值;  # 全局块,主要设置 Nginx 服务器整体运行的配置指令

# events 块,主要设置 Nginx 服务器与用户的网络连接,这一部分对 Nginx 服务器的性能影响较大
events {
指令名 指令值;
}

# http 块,是 Nginx 服务器配置中的重要部分,代理、缓存、日志记录、第三方模块配置...
http {
指令名 指令值;

server { # server 块,是 Nginx 配置和虚拟主机相关的内容
指令名 指令值;
location / { # location 块,基于 Nginx 服务器接收请求字符串与 location 后面的值进行匹配,对特定请求进行处理
指令名 指令值;
}
}
...
}

小结

nginx.conf 配置文件中默认有三大块:全局块、events 块、http 块

http 块中可以配置多个 server 块,每个 server 块又可以配置多个 location 块。

全局块

全局块的配置影响 Nginx 的全局设置。如用户权限,启动的进程数等。

user指令

  1. user:用于配置运行 Nginx 服务器的 worker 进程的用户和用户组。
语法 默认值 位置
user <user> [group] nobody 全局块

该属性也可以在编译的时候指定,语法如下 ./configure --user=user --group=group,如果两个地方都进行了设置,最终生效的是配置文件中的配置。

该指令的使用步骤:

  1. 进入配置文件添加一个用户信息 『 www 』
1
user www

测试进行测试配置文件会报错:

1
nginx -t
1
2
3
[root@master conf]# nginx -t
nginx: [emerg] getpwnam("www") failed in /usr/local/nginx/conf/nginx.conf:1
nginx: configuration file /usr/local/nginx/conf/nginx.conf test failed

原因在于 Linux 系统不存在 www 用户,我们需要创建它。

  1. 创建一个用户
1
useradd www
  1. 重启 Nginx 的配置文件
1
2
3
4
nginx -s reload

# 查看重启是否生效
ps -ef | grep nginx

最后返回的结果由 root 用户改为 www 用户,代表配置成功。

1
2
3
4
[root@master conf]# ps -ef | grep nginx
root 8960 1 0 16:13 ? 00:00:00 nginx: master process ./nginx
www 11975 8960 0 20:44 ? 00:00:00 nginx: worker process
root 11978 10615 0 20:44 pts/1 00:00:00 grep --color=auto nginx
  1. 在 Linux 的 /root 下创建一个 html 目录,并且进入 html 目录,创建 index.html 文件
1
2
3
4
5
mkdir /root/html

cd /root/html

vim index.html

然后在 /root/html/index.html 文件里添加如下内容:

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
<!DOCTYPE html>
<html>
<head>
<title>Welcome to nginx!</title>
<style>
body {
width: 35em;
margin: 0 auto;
font-family: Tahoma, Verdana, Arial, sans-serif;
}
</style>
</head>
<body>
<h1>Welcome to nginx!</h1>
<p>If you see this page, the nginx web server is successfully installed and
working. Further configuration is required.</p>

<p>For online documentation and support please refer to
<a href="http://nginx.org/">nginx.org</a>.<br/>
Commercial support is available at
<a href="http://nginx.com/">nginx.com</a>.</p>

<p><em>Thank you for using nginx.</em></p>
<p><em>I am WWW</em></p>
</body>
</html>

这些内容其实就是在 Nginx 的欢迎页面上多加别 I am WWW 内容。

  1. 修改 nginx.conf
1
2
3
4
5
location / {
# root html; # 原始的代码注释掉
root /root/html; # 不再是 html 目录,而是 root 下的 html 目录
index index.html index.htm; # 就是上方创建的 index.html
}
  1. nginx -s reload重新加载后,测试启动访问

image

页面会报 403 拒绝访问的错误。

  1. 分析原因:因为当前用户(www 用户)没有访问 /root/html 目录的权限,这个目录只有 root 才能访问。

那么 www 如何访问我们写的 index.html 页面呢?我们知道,每新建一个用户,/home 下都会生成该用户权限的目录。

  1. 将文件移动到 /home/www/html/index.html
1
mv /root/html /home/www
  1. 记得修改配置文件的资源内容
1
2
3
4
5
6
location / {
# root html; # 原始的代码注释掉
# root /root/html; # 这属于 root 权限的页面,注释或者删除掉
root /home/www/html; # 这是 www 用户有权限访问的目录
index index.html index.htm; # 访问了 html 目录,然后访问 index.html 文件
}
  1. 再次测试启动访问,可以正常访问。

综上所述,使用 user 指令可以指定启动运行工作进程的用户及用户组,这样对于系统的权限访问控制的更加精细,也更加安全。

我们也能理解了配置文件的 locaotion 块的基本使用,root 对应着访问目录,index 对应着访问目录下的默认页面。

image

work process指令

  1. master_process 指令用来指定是否开启 worker 工作进程。

如果为 off,则代表关闭了 worker 进程,这时候启动 Nginx,只有 master 进程启动,没有 worker 进程。默认开启 worker 工作进程。(需要重启nginx服务生效)

语法 默认值 位置
master_process <on | off>; master_process on; 全局块
  1. worker_processes 指令用于配置 Nginx 生成 worker 工作进程的数量,这个是 Nginx 服务器实现并发处理

    服务的关键所在。

理论上来说 workder process 的值越大,可以支持的并发处理量也越多,但事实上这个值的设定是需要受到来自服务器自身的限制,建议将该值和服务器 CPU 的内核数保存一致。

语法 默认值 位置
worker_processes <num | auto>; 1 全局块

如果将 worker_processes 设置成 2,则会看到如下内容:

1
2
3
4
5
[root@master conf]# ps -ef | grep nginx
root 8960 1 0 16:13 ? 00:00:00 nginx: master process ./nginx
www 12299 8960 0 21:14 ? 00:00:00 nginx: worker process
www 12300 8960 0 21:14 ? 00:00:00 nginx: worker process
root 12302 10615 0 21:14 pts/1 00:00:00 grep --color=auto nginx

出现两个 worker 工作进程。

其他指令

  1. daemon 指令设置 Nginx 是否以守护进程的方式启动。on 代表开启守护进程,off 代表关闭守护进程,默认开启。(需要重启nginx生效)

守护式进程是 Linux 后台执行的一种服务进程,特点是 独立于控制终端,不会随着终端关闭而停止,也就是后台启动。

语法 默认值 位置
daemon <on | off>; daemon on; 全局块
  1. pid 指令用来配置 Nginx 当前 master 进程的进程号 ID 存储的文件路径。默认路径是 /usr/local/nginx/logs/nginx.pid
语法 默认值 位置
pid <file>; /usr/local/nginx/logs/nginx.pid 全局块

该属性也可以通过 ./configure --pid-path=PATH 在编译时来指定。

  1. error_log 指令用来配置 Nginx 的错误日志存放路径。默认路径是 /usr/local/nginx/logs/error.log
语法 默认值 位置
error_log <file> [日志级别]; error_log logs/error.log error; 全局块、http、server、location

该属性也可以通过 ./configure --error-log-path=PATH 在编译时来指定。

其中日志级别的值有『 debug | info | notice | warn | error | crit | alert | emerg 』,翻译过来为「调试 | 信息 | 通知 | 警告 | 错误 | 临界 | 警报 | 紧急」,这块建议大家设置的时候不要设置成 info 以下的等级,因为会带来大量的磁盘 I/O 消耗,影响 Nginx 的性能。

  1. include 指令用来引入其他的配置文件,使 Nginx 的配置更加灵活。
语法 默认值 位置
include <file>; any

events块

events指令

  1. accept_mutex 指令用来设置是否开启 Nginx 网络连接序列化。默认开启。
语法 默认值 位置
accept_mutex <on | off>; accept_mutex on; events

这个配置主要可以用来解决常说的「惊群」问题。大致意思是在某一个时刻,客户端发来一个请求连接,Nginx 后台是以多进程的工作模式,也就是说有多个 worker 进程会被同时唤醒,但是最终只会有一个进程可以获取到连接,如果每次唤醒的进程数目太多,就会影响 Nginx 的整体性能。如果将上述值设置为 on (开启状态),将会对多个 Nginx 进程接收连接进行序列号,一个个来唤醒接收,就防止了多个进程对连接的争抢。

如图的小狗,如果只是一块「骨头」出现,则只需要唤醒一个小狗即可(开启 on),如果多个「骨头」如三个同时出现,那么唤醒三个小狗效率更高(此时需要设置 off)

image

  1. multi_accept 指令用来设置是否开启同时接收多个网络连接。默认开启。
语法 默认值 位置
multi_accept <on | off>; multi_accept off; events

如果 multi_accept 被禁止了,Nginx 的一个工作进程只能同时接受一个新的连接。如果开启,一个工作进程可以同时接受所有的新连接。建议开启。

  1. worker_connections 指令用来配置单个 worker 进程最大的连接数。默认 512 个连接数。
语法 默认值 位置
worker_connections <number>; worker_commections 512; events

这里的连接数不仅仅包括和前端用户建立的连接数,而是包括所有可能的连接数。另外,number 值不能大于操作系统支持打开的最大文件句柄数量。

  1. use 指令用来设置 Nginx 服务器选择哪种事件驱动来处理网络消息。
语法 默认值 位置
use <method>; 根据操作系统规定 events

注意:此处所选择事件处理模型是 Nginx 优化部分的一个重要内容,method 的可选值有『 select | poll | epoll | kqueue 』等,之前在准备 Centos 环境的时候,我们强调过要使用 Linux 内核在 2.6 以上,就是为了能使用 epoll 函数来优化 Nginx。

另外这些值的选择,我们也可以在编译的时候使用 --with-select_module--without-select_module--with-poll_module--without-poll_module 来设置是否需要将对应的事件驱动模块编译到 Nginx 的内核。

events指令配置模板

打开 Nginx 的配置文件 nginx.conf,添加如下配置

1
2
3
4
5
6
events{
accept_mutex on; # 开启 Nginx 网络连接序列化
multi_accept on; # 开启同时接收多个网络连接
worker_commections 1024; # 单个 worker 进程最大的连接数
use epoll; # 使用 epoll 函数来优化 Nginx
}

启动测试

1
2
3
4
5
# 测试配置是否语法出错
nginx -t

# 重新加载 Nginx
nginx -s reload

http块

定义MIME-Type

我们都知道浏览器中可以显示的内容有 HTML、XML、GIF 等种类繁多的文件、媒体等资源,浏览器为了区分这些资源,就需要使用 MIME Type。所以说 MIME Type 是网络资源的媒体类型。Nginx 作为 Web 服务器,也需要能够识别前端请求的资源类型。

在 Nginx 的配置文件中,默认有两行配置:

1
2
include mime.types;      # 引入 mime.types 文件的内容
default_type application/octet-stream; # 默认的 MIME 类型
  1. default_type 指令用来配置 Nginx 响应前端请求默认的 MIME 类型。默认是 text 文本。
语法 默认值 位置
default_type <mime-type>; default_type text/plain; http、server、location

default_type 前面还有一句 include mime.types,include 之前我们已经介绍过,相当于把 mime.types 文件中 MIMT 类型与相关类型文件的文件后缀名的对应关系加入到当前的配置文件中。

举例来说明:

有些时候请求某些接口的时候需要返回指定的文本字符串或者 json 字符串,而不是页面,如果逻辑非常简单或者干脆是固定的字符串,那么可以使用 Nginx 快速实现,这样就不用编写程序响应请求了,可以减少服务器资源占用并且响应性能非常快。

如何实现:

1
2
3
4
5
6
7
8
location /get_text {
default_type text/html; # 等价于 text/plain,返回文本类型
return 200 "<h1>This is nginx's text</h1>";
}
location /get_json{
default_type application/json; # 返回 json 字符串类型
return 200 '{"name": "xiaoming", "age": 21}';
}

image

image

自定义服务日志

Nginx 中日志的类型分 access.log、error.log。

access.log 日志用来记录用户所有的访问请求。

error.log 日志记录 Nginx 本身运行时的错误信息,不会记录用户的访问请求。

Nginx 服务器支持对服务日志的格式、大小、输出等进行设置,需要使用到两个指令,分别是 access_loglog_format 指令。

  1. access_log 指令用来设置用户访问日志的相关属性。
语法 默认值 位置
access_log <path> [format[buffer=size]]; access_log logs/access.log combined; http、server、location

format 对应着 log_format 的 name,必须保持一致。

  1. log_format 指令用来指定日志的输出格式。
语法 默认值 位置
log_format <name> [escape=default | json | none] <string> …… ; log_format combined “…”; http

name 对用 access_log 的 format,必须保持一致。

例子 1:自定义日志路径和输出格式

  • /usr/local/nginx/logs 下创建 my.log 文件,该文件作为日志。
1
mkdir /usr/local/nginx/logs/my.log
  • 自定义日志输出格式:==========>This is My format
  • 在配置文件配置相关指令
1
2
log_format myformat '=========>This is My format';
access_log logs/my.log myformat;
  • 重启服务并进行测试
1
2
3
4
5
# 重启 Nginx 服务
nginx -s reload

# 监听日志
tail -f /usr/local/nginx/logs/my.log

浏览器访问一次 Nginx 的欢迎页面,回来看日志的输出,结果如图:

image

例子 2:输出内容加上访问机器的信息

  • 进入配置文件,在输出格式上加上 Nginx 的内置参数
1
2
log_format myformat '=========>This is My format:$http_user_agent';
access_log logs/my.log myformat;
  • 重启测试
1
2
3
4
5
# 重启 Nginx 服务
nginx -s reload

# 监听日志
tail -f /usr/local/nginx/logs/my.log

浏览器访问一次 Nginx 的欢迎页面,回来看日志的输出,结果如图:

image

其他配置指令

  1. sendfile:用来设置 Nginx 服务器是否使用 sendfile 传输文件,该属性可以大大提高 Nginx 处理静态资源的性能。默认关闭,建议开启。
语法 默认值 位置
sendfile <on | off>; sendfile off; http、server、location
  1. keepalive_timeout:用来设置长连接的超时时间,默认超时时间是 75 秒。

为什么要使用 keepalive?

我们都知道 HTTP 是一种无状态协议,客户端向服务端发送一个 TCP 请求,服务端响应完毕后断开连接。

如何客户端向服务端发送多个请求,每个请求都需要重新创建一次连接,效率相对来说比较多,使用 keepalive 模式,可以告诉服务器端在处理完一个请求后保持这个 TCP 连接的打开状态,若接收到来自这个客户端的其他请求,服务端就会利用这个未被关闭的连接,而不需要重新创建一个新连接,提升效率,但是这个连接也不能一直保持,这样的话,连接如果过多,也会是服务端的性能下降,这个时候就需要我们进行设置其的超时时间。

语法 默认值 位置
keepalive_timeout <time>; keepalive_timeout 75s; http、server、location
  1. keepalive_requests:用来设置一个 keep-alive 连接使用的次数,默认是 100 次。
语法 默认值 位置
keepalive_requests <number>; keepalive_requests 100; http、server、location

server块和location块

server 块和 location 块都是我们要重点学习的内容,因为我们后面会对 Nginx 的功能进行详细讲解,所以该内容在静态资源部署静态资源访问进行详细说明。

本次我们这是认识下 Nginx 默认给的 nginx.conf 中的相关内容,以及 server 块与 location 块在使用的时候需要注意的一些内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
listen 80; # 监听 80 端口,如果更改端口,则外界访问的时候带上对应的端口号,如 8080
server_name localhost; # 指定可以访问 Nginx 的 IP 地址

location / {
root html; # 访问资源所对应的目录,这里是 html 目录
index index.html index.htm; # 访问资源所对应目录下的默认页面,优先级递增
}

error_page 500 502 503 504 404 /50x.html; # 访问错误,跳转访问 /50x.html 请求
location = /50x.html { # 访问 /50x.html 请求的处理
root html; # 访问资源所对应的目录,这里是 html 目录的 50x.html
}
}
  • listen 和 server_name 是我们的 http://server_name:listen,如 http://localhost:80
  • location / 就是访问 http://server_name:listen/,里面的配置对应着 http://server_name:listen/html/index.html
  • 页面产生 500 502 503 504 404,就会发送 http://server_name:listen/50x.html
  • location = /50x.html 就是 http://server_name:listen/50x.html,它会自动访问 http://server_name:listen/html/50x.html
  • root 代表资源目录指令
  • index 代表默认访问网页指令

Nginx 静态资源部署

Nginx 静态资源部署

引言

静态资源如何部署?本内容带你了解静态资源相关的操作和内容。

静态资源指令配置、静态资源优化配置、静态资源压缩配置、静态资源缓存配置。

Nginx静态资源概述

上网去搜索访问资源对于我们来说并不陌生,通过浏览器发送一个 HTTP 请求实现从客户端发送请求到服务器端获取所需要内容后并把内容回显展示在页面的一个过程。这个时候,我们所请求的内容就分为两种类型,一类是静态资源、一类是动态资源。

静态资源即指在服务器端真实存在并且能直接拿来展示的一些文件,比如常见的 html 页面、css 文件、js 文件、图片、视频等资源;

动态资源即指在服务器端真实存在但是要想获取需要经过一定的业务逻辑处理,根据不同的条件展示在页面不同这 一部分内容,比如说报表数据展示、根据当前登录用户展示相关具体数据等资源;

Nginx 处理静态资源的内容,我们需要考虑下面这几个问题:

  • 静态资源的配置指令
  • 静态资源的配置优化
  • 静态资源的压缩配置指令
  • 静态资源的缓存处理
  • 静态资源的访问控制,包括跨域问题和防盗链问题

静态资源指令配置

listen指令

该指令是用来配置监听端口。默认监听 80(root 启动 Nginx) 和 8000(非 root 启动 Nginx) 端口。

语法 默认值 位置
listen <address>[:port] [default_server] …… ;
listen <port> [default_server] …… ;
listen *:80 | *:8000 server

listen 指令的设置比较灵活,我们通过几个例子来把常用的设置方式熟悉下:

1
2
3
4
listen 127.0.0.1:8000; # listen localhost:8000 监听指定的IP和端口
listen 127.0.0.1; # 监听指定IP的所有端口
listen 8000; # 监听指定端口上的连接
listen *:8000; # 监听指定端口上的连接

default_server 属性是标识符,用来将此虚拟主机设置成默认主机。所谓的默认主机指的是如果没有匹配到对应的 address:port,则会执行默认的 server。如果不指定该标识符,又没有匹配到对应的 address:port 时,默认使用的是第一个 server,所以第一个 server 要好好设置,建议第一个 server 就加上 default_server

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server{
listen 8080;
server_name 127.0.0.1;
location / {
root html;
index index.html;
}
}
server{
listen 8080 default_server;
server_name localhost;
default_type text/plain;
return 444 'This is a error request';
}

此时访问 8080 端口,它会访问第二个 server,如果第二个 server 去掉 default_server ,则默认访问第一个 server。

image

server_name指令

该指令用来设置虚拟主机服务名称。默认为空。

比如 127.0.0.1、localhost、域名[www.baidu.com | www.jd.com]。

语法 默认值 位置
server_name <name> …… ; server_name “”; server
  • name 可以提供多个中间用空格分隔。

关于 server_name 的配置方式有三种,分别是:

  • 精确匹配
  • 通配符匹配
  • 正则表达式匹配

配置方式一:精确匹配

如:

1
2
3
4
5
server {
listen 80;
server_name www.frx.com www.bing.com;
...
}

此时以 www.frx.com 或者 www.bing.com 域名进行访问,就会跳转到 Nginx 的欢迎页面,前提是你需要拥有该域名,并且该域名和 Nginx 所在的系统 IP 进行绑定。

所以我可以利用 hosts 文件进行「模拟」域名。

补充小知识点

hosts 是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应的IP地址建立一个关联「数据库」,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从 hosts 文件中寻找对应的IP地址,一旦找到,系统会立即打开对应网页,如果没有找到,则系统会再将网址提交 DNS 域名解析服务器进行 IP 地址的解析。

hosts 文件不同系统的位置:

  • 在 windows 的位置:C:\Windows\System32\drivers\etc
  • 在 centos 的位置:/etc/hosts

因为域名是要收取一定的费用,所以我们可以使用修改 hosts 文件来制作一些虚拟域名来使用。需要修改 /etc/hosts 文件来添加

1
2
3
4
5
6
# 进入 hosts 文件
vim /etc/hosts

# 添加内容
127.0.0.1 www.frx.com
127.0.0.1 www.bing.com
  • 访问测试

image

配置方式二:使用通配符配置

server_name 指令支持通配符 *,但需要注意的是通配符不能出现在域名的中间,只能出现在首段或尾段,如:

1
2
3
4
5
6
server {
listen 80;
server_name *.frx.com www.frx.*;
# www.frx.cn abc.frx.cn www.bing.cn www.frx.com
...
}

下面的配置就会报错,因为 * 不能出现在域名的中间和与其他字符串联使用

1
2
3
4
5
server {
listen 80;
server_name www.*.cn www.frx.c*
...
}
1
2
3
4
5
server {
listen 80;
server_name .frx.com
...
}

.frx.com 相当于 *..frx.com + frx.com

配置三:使用正则表达式配置

server_name 指令可以使用正则表达式,并且使用 ~ 作为正则表达式字符串的开始标记。

常见的正则表达式:

代码 说明
^ 匹配搜索字符串开始位置
$ 匹配搜索字符串结束位置
. 匹配除换行符 \n 之外的任何单个字符
\ 转义字符,将下一个字符标记为特殊字符
[xyz] 字符集,与任意一个指定字符匹配
[a-z] 字符范围,匹配指定范围内的任何字符
\w 与以下任意字符匹配 A-Z a-z 0-9 和下划线,等效于[A-Za-z0-9_]
\d 数字字符匹配,等效于[0-9]
{n} 正好匹配 n 次
{n,} 至少匹配 n 次
{n,m} 匹配至少 n 次至多 m 次
* 零次或多次,等效于{0,}
+ 一次或多次,等效于{1,}
? 零次或一次,等效于{0,1}

配置如下:

1
2
3
4
5
6
server{
listen 80;
server_name ~^www\.(\w+)\.com$;
default_type text/plain;
return 200 $1;
}

注意 ~ 后面不能加空格。括号代表可以在 Ngxin 配置文件内获取其中的值,如上方的 (\w+) 的内容可以用 $1 获取到参数,如果有多个括号,依次使用 $2 $3 …… 获取。

比如现在访问 http://www.frx.com,则返回 frx 到页面上,因为frx 被放在 $1 处:return 200 frx

⚠ 这里进行说明:server_name 配置了 localhost 和配置 IP 或者域名的区别

localhost 是「虚拟 IP」,如果不是本机访问,而是外界访问,那么这个就是无效的,但是为什么学习阶段的时候都不改呢,因为当 Nginx 没有匹配到指定的 server_name,默认找到第一个 server 块,而 Nginx 默认的第一个 server 块就是 localhost,哪怕你把 localhost 改为其他的,如 hello,它也能访问。只是因为在匹配不到的情况下,默认是第一个 service 块,哪怕它确实和访问的 IP 不匹配。

IP 如果和域名进行了绑定,那么在 server_name 中,两者都可以填写,填了域名,最后也是找到 IP,只是因为域名好记住。

知道了区别,在生产环境上,可以给个指定的错误页面。如果匹配不上,则返回友好的提示,如第一个 server 块:

1
2
3
4
5
6
7
8
9
10
11
12
13
server{
listen 80 default_server;
server_name _; # 匹配不上的时候,也可以填写 localhost
return "<h1>不好意思,匹配不到!";
# return 403; # 也可以直接返回错误码
#
}

server{
listen 80;
server_name www.frxcat.fun; # 匹配域名访问
# ......
}

上面代码块只允许域名访问,而不允许 IP 访问,避免其他人把未备案的域名解析到自己的服务器 IP。

当然你也可以不设置错误页面。因为 Nginx 匹配不上时,直接返回它的欢迎界面。

匹配执行顺序

由于 server_name 指令支持通配符和正则表达式,因此在包含多个虚拟主机的配置文件中,可能会出现一个名称被多个虚拟主机的 server_name 匹配成功,当遇到这种情况,当前的请求交给谁来处理呢?如下:

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
31
32
33
34
server{
listen 80;
server_name ~^www\.\w+\.com$;
default_type text/plain;
return 200 'regex_success';
}

server{
listen 80;
server_name www.frx.*;
default_type text/plain;
return 200 'wildcard_after_success';
}

server{
listen 80;
server_name *.frx.com;
default_type text/plain;
return 200 'wildcard_before_success';
}

server{
listen 80;
server_name www.frx.com;
default_type text/plain;
return 200 'exact_success';
}

server{
listen 80 default_server;
server_name _;
default_type text/plain;
return 444 'default_server not found server';
}

访问 http://www.kele.com 的优先级:(访问完后请注释掉处理这个请求的 server)

  • exact_success(第一个是精确匹配,然后注释掉该 server)
  • wildcard_before_success(第二个是开始匹配符,然后注释掉该 server)
  • wildcard_after_success(第三个是结尾匹配符,然后注释掉该 server)
  • regex_success(第四个是正则表达式,然后注释掉该 server)
  • default_server not found server!!(第五个是默认 server,因为前四个都注释了,所以只能走默认的 server)

结论

  • No1:准确匹配 server_name

  • No2:通配符在开始时匹配 server_name 成功

  • No3:通配符在结束时匹配 server_name 成功

  • No4:正则表达式匹配 server_name 成功

  • No5:被默认的 default_server 处理,如果没有指定默认找第一个 server

location指令

location 指令是用来设置请求的 URI。

1
2
3
4
5
6
7
8
9
10
11
server{
listen 80;
server_name localhost;
location / {

}
location /abc {

}
...
}
语法 默认值 位置
location [ = | ~ | * | ^ | @ ] <uri>{…} server、location

uri 变量是待匹配的请求字符串,可以不包含正则表达式,也可以包含正则表达式,那么 Nginx 服务器在搜索匹配 location 的时候,是先使用不包含正则表达式进行匹配,找到一个匹配度最高的一个,然后在通过包含正则表达式的进行匹配,如果能匹配到直接访问正则表达式的,匹配不到,就使用刚才匹配度最高(前缀最长的)的那个 location 来处理请求

属性介绍:

不带符号,要求必须以指定模式开头,但是不要求精确匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
listen 80;
server_name 127.0.0.1;
location /abc {
default_type text/plain;
return 200 "access success";
}
}

# 以下访问都是正确的
http://192.168.91.200/abc
http://192.168.91.200/abc?p1=kele
http://192.168.91.200/abc/
http://192.168.91.200/abcdef

= 是用于不包含正则表达式的 uri,必须与指定的模式精确匹配

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
listen 80;
server_name 127.0.0.1;
location =/abc {
default_type text/plain;
return 200 "access success";
}
}
# 匹配成功
http://192.168.91.200/abc
http://192.168.91.200/abc?p1=TOM
# 匹配失败
http://192.168.91.200/abc/
http://192.168.91.200/abcdef

~ 是用于表示当前 uri 中包含了正则表达式,并且区分大小写

~* 是用于表示当前 uri 中包含了正则表达式,但是是不区分大小写

换句话说,如果 uri 包含了正则表达式,需要用上述两个符号来标识

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 使用正则表达式,区分大小写
server {
listen 80;
server_name 127.0.0.1;
location ~^/abc\w$ {
default_type text/plain;
return 200 "access success";
}
}

# 使用正则表达式,不区分大小写
server {
listen 80;
server_name 127.0.0.1;
location ~*^/abc\w$ {
default_type text/plain;
return 200 "access success";
}
}

^~ 是用于不包含正则表达式的 uri,功能和不加符号的一致,唯一不同的是,如果请求匹配上了,那么就停止搜索其他模式了。

1
2
3
4
5
6
7
8
server {
listen 80;
server_name 127.0.0.1;
location ^~/abc {
default_type text/plain;
return 200 "access success";
}
}

@ 前缀可以用来定义一个命名的 location,该 location 不处理正常的外部请求,一般用来当作标识供内部重定向使用。它们不能嵌套,也不能包含嵌套的 location。

1
2
3
4
5
6
7
8
9
10
11
12
location /try {
try_files $uri $uri/ @name;
}

location /error {
error_page 404 = @name;
return 404;
}

location @name {
return 200 "@name";
}

这时访问 /try 或者 /error 都会返回 @name

root/alias指令

  • root 指令是设置请求资源的根目录。默认值是 html。
语法 默认值 位置
root <path>; root html; http、server、location

path 是 Nginx 服务器接收到请求以后查找资源的根目录路径。

  • alias 指令是用来更改 location 的 URI。
语法 默认值 位置
alias <path>; location
  • path 是修改后的根路径。

以上两个指令都可以来指定访问资源的路径,那么这两者之间的区别是什么?

举例说明

  1. /usr/local/nginx/html 目录下创建一个 images 目录,并在目录下放入一张图片 mv.png 图片。

    然后进入配置文件,添加如下内容:

1
2
3
location /images {
root /usr/local/nginx/html;
}

访问图片的路径为:http://192.168.91.200/images/mv.png

  1. 如果把root改为alias
1
2
3
location /images {
alias /usr/local/nginx/html;
}

再次访问上述地址,页面会出现 404 的错误,查看错误日志会发现是因为地址不对,所以验证了:

  • root 的处理结果是:root 路径 + location 路径,location 路径包括匹配后面的请求,即包括 /mv.png

    /usr/local/nginx/html/images/mv.png

  • alias 的处理结果是:使用 alias 路径替换 location 路径,但是不会替换匹配后面的请求,即不会替换 /mv.png

需要在 alias 后面路径改为:

1
2
3
location /images {
alias /usr/local/nginx/html/images;
}
  1. 如果 location 路径是以 / 结尾,则 alias 也必须是以 / 结尾,root 没有要求。

将上述配置修改为:

1
2
3
location /images/ {
alias /usr/local/nginx/html/images;
}

访问就会出问题,查看错误日志还是路径不对,所以需要把 alias 后面加上 /

小结:

  • root 的处理结果是: root 路径 + location 路径
  • alias 的处理结果是:使用 alias 路径替换 location 路径
  • alias 是一个目录别名的定义,root 则是最上层目录的含义
  • 如果 location 路径是以 / 结尾,则 alias 也必须是以 / 结尾,root 没有要求
    • alias 不支持 location 的 =

这里再多言几句,alias 后指定的资源路径,Nginx 就会去这个路径下找资源,「忽略」location 本身的的请求,仅拼接 location 后面的请求。如果你想去拼接 location 本身,就用绝对路径(包括 location)的alias。

index指令

index 指令是设置网站的默认首页。默认是 index.html。

语法 默认值 位置
index <file> …; index index.html; http、server、location

index 后面可以跟多个设置,如果访问的时候没有指定具体访问的资源,则会从左往右依次进行查找,找到第一个为止。

举例说明:

1
2
3
4
location / {
root /usr/local/nginx/html;
index index.html index.htm;
}

访问该 location 的时候,可以通过 http://ip:port/ 访问,地址后面如果不添加任何内容,则默认依次访问 index.html 和 index.htm,找到第一个来进行返回。

error_page指令

error_page 指令是设置网站的错误页面。

语法 默认值 位置
error_page <code> …… [=[response]] <uri>; http、server、location ……

code 是响应码。

当出现对应的响应 code 后,如何来处理?

举例说明

  1. 可以指定具体跳转的地址
1
2
3
server {
error_page 404 http://www.frx.com;
}

当页面产生 404 时,自动跳转到 http://www.frx.com

  1. 可以指定重定向地址
1
2
3
4
5
6
7
server{
error_page 404 /50x.html;
error_page 500 502 503 504 /50x.html;
location =/50x.html {
root html;
}
}

产生错误页面时,重定向到 /50x.html,然后触发 location,最终访问的是 html 目录下的 50x.html 页面

  1. 使用 location 的 @ 符合完成错误信息展示
1
2
3
4
5
6
7
server{
error_page 404 @jump_to_error;
location @jump_to_error {
default_type text/plain;
return 404 'Not Found Page...';
}
}

可选项 =[response] 的作用是用来将相应代码更改为另外一个,如下:

1
2
3
4
5
6
server{
error_page 404 =200 /50x.html;
location =/50x.html {
root html;
}
}

这样的话,当返回 404 找不到对应的资源的时候,在浏览器上可以看到,最终返回的状态码是 200 而不是 404,这块需要注意下,编写 error_page 后面的内容,404 后面需要加空格,200 前面不能加空格。

静态资源优化配置

Nginx 对静态资源如何进行优化配置。这里从三个属性配置进行优化:

1
2
3
sendfile on;
tcp_nopush on;
tcp_nodeplay on;

建议三个都开启。如果想知道为什么,请往下看。

sendfile

该指令是用来开启高效的文件传输模式。默认关闭,建议开启。

语法 默认值 位置
sendfile <on | off>; sendfile off; http、server、location ……

请求静态资源的过程:客户端通过网络接口向服务端发送请求,操作系统将这些客户端的请求传递给服务器端应用程序,服务器端应用程序会处理这些请求,请求处理完成以后,操作系统还需要将处理得到的结果通过网络适配器传递回去。

如:

1
2
3
4
5
6
7
8
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html;
}
}

假设在 html 目录下有一个 welcome.html 页面,访问地址:http://192.168.91.200/welcome.html

流程如下:

image

image

tcp_nopush

该指令必须在 sendfile 打开的状态下才会生效,主要是用来提升网络包的传输「效率」。默认关闭。

语法 默认值 位置
tcp_nopush <on | off>; tcp_nopush off; http、server、location

tcp_nodelay

该指令必须在 keep-alive 连接开启的情况下才生效,来提高网络包传输的「实时性」。默认开启。

语法 默认值 位置
tcp_nodelay <on | off>; tcp_nodelay on; http、server、location

image

tcp_nopush 就像大巴车,等所有旅客占满了座位,才开始发车到景点(客户端),而 tcp_nodelay,上来一个旅客,就马上发车到景点客户端)。

优化总结

经过分析,『 tcp_nopush 』和『 tcp_nodelay 』看起来是「互斥的」,那么为什么要将这两个值都打开,这个大家需要知道的是在 Linux2.5.9 以后的版本中两者是可以兼容的,三个指令都开启的好处是,sendfile 可以开启高效的文件传输模式,『 tcp_nopush 』开启可以确保在发送到客户端之前数据包已经充分「填满」,这大大减少了网络开销,并加快了文件发送的速度。然后,当它到达最后一个可能因为没有「填满」而暂停的数据包时,Nginx 会忽略『 tcp_nopush 』参数, 然后,『 tcp_nodelay 』强制套接字发送数据。由此可知,『 tcp_nopush 』可以与『 tcp_nodelay 』一起设置,它比单独配置『 tcp_nodelay 』具有更强的性能。

所以回归开头,我们可以使用如下配置来优化 Nginx 静态资源的处理:

1
2
3
4
# 三个都开启
sendfile on;
tcp_nopush on;
tcp_nodelay on;

静态资源压缩配置

经过上述内容的优化,我们再次思考一个问题,假如在满足上述优化的前提下,我们传送一个 1M 的数据和一个 10M 的数据那个效率高?答案显而易见,传输内容小,速度就会快。那么问题又来了,同样的内容,如果把大小降下来,我们脑袋里面要蹦出一个词就是「压缩」,接下来,我们来学习 Nginx 的静态资源压缩模块。

在 Nginx 的配置文件中可以通过配置 gzip 来对静态资源进行压缩,相关的指令可以配置在 http 块、server 块和 location 块中,Nginx 可以通过对这些指令进行解析和处理:

  • ngx_http_gzip_module 模块
  • ngx_http_gzip_static_module 模块
  • ngx_http_gunzip_module 模块

接下来我们从以下内容进行学习:

  • Gzip 各模块支持的配置指令
  • Gzip 压缩功能的配置
  • Gzip 和 sendfile 的冲突解决
  • 浏览器不支持 Gzip 的解决方案

Gzip模块配置指令

接下来所学习的指令都来自 ngx_http_gzip_module 模块,该模块会在 Nginx 安装的时候内置到 Nginx 的安装环境中,也就是说我们可以直接使用这些指令。

  • gzip 指令是用于开启或者关闭 Gzip 功能。默认关闭
语法 默认值 位置
gzip <on | off>; gzip off; http、server、location ……

注意:只有该指令为打开状态,下面的指令才有效果

1
2
3
http{
gzip on;
}
  • gzip_types 指令可以根据响应页的 MIME 类型选择性地开启 Gzip 压缩功能。默认是 text/html
语法 默认值 位置
gzip_types <mime-type> …… ; gzip_types text/html; http、server、location

所选择的值可以从 mime.types 文件中进行查找,也可以使用 * 代表所有。

1
2
3
4
5
http{
gzip_types application/javascript;
# * 代表所有
gzip_types *
}
  • gzip_comp_level 指令是用于设置 Gzip 压缩程度,级别从 1-9,1 表示要是程度最低,要是效率最高,9 刚好相反,压缩程度最高,但是效率最低、最费时间。默认值是 1
语法 默认值 位置
gzip_comp_level <level>; gzip_comp_level 1; http、server、location
1
2
3
http{
gzip_comp_level 6;
}
  • gzip_vary 指令是用于设置使用 Gzip 进行压缩发送是否携带『Vary:Accept-Encoding』头域的响应头部。主要是告诉接收方,所发送的数据经过了 Gzip 压缩处理。默认关闭
语法 默认值 位置
gzip_vary <on | off>; gzip_vary off; http、server、location

image

  • gzip_buffers 指令是用于处理请求压缩的缓冲区数量和大小
语法 默认值 位置
gzip_buffers <number> <size>; gzip_buffers 32 4k | 16 8k; http、server、location

其中 number 是指定 Nginx 服务器向系统申请缓存空间个数,size 指的是每个缓存空间的大小。主要实现的是申请 number 个每个大小为 size 的内存空间。这个值的设定一般会和服务器的操作系统有关,所以建议此项不设置,使用默认值即可。

1
gzip_buffers 4 16K;	  # 缓存空间大小
  • gzip_disable 指令是针对不同种类客户端发起的请求,可以选择性地开启和关闭 Gzip 功能
语法 默认值 位置
gzip_disable <regex> …… ; http、server、location

regex 是根据客户端的浏览器标志(user-agent)来设置,支持使用正则表达式。指定的浏览器标志不使用 Gzip.该指令一般是用来排除一些明显不支持 Gzip 的浏览器。

1
gzip_disable "MSIE [1-6]\.";

gzip_http_version 指令是针对不同的 HTTP 协议版本,可以选择性地开启和关闭 Gzip 功能。默认是 1.1 版本

语法 默认值 位置
gzip_http_version <1.0 | 1.1>; gzip_http_version 1.1; http、server、location

该指令是指定使用 Gzip 的 HTTP 最低版本,该指令一般采用默认值即可。

  • gzip_min_length 指令是针对传输数据的大小,可以选择性地开启和关闭 Gzip 功能
语法 默认值 位置
gzip_min_length <length>; gzip_min_length 20; http、server、location

Nignx 计量大小的单位:bytes [字节] / kb [千字节] / M [兆]

例如: 1024 / 10k | K / 10m | M

Gzip 压缩功能对大数据的压缩效果明显,但是如果要压缩的数据比较小的话,可能出现越压缩数据量越大的情况,因此我们需要根据响应内容的大小来决定是否使用 Gzip 功能,响应页面的大小可以通过头信息中的 Content-Length 来获取。但是如何使用了 Chunk 编码动态压缩,该指令将被忽略。建议设置为 1K 或以上。

  • gzip_proxied 指令设置是否对服务端返回的结果进行 Gzip 压缩
语法 默认值 位置
gzip_proxied <off | expired | no-cache | no-store | private | no_last_modified | no_etag | auth | any>; gzip_proxied off; http、server、location
  • off:关闭 Nginx 服务器对后台服务器返回结果的 Gzip 压缩
  • expired:如果 header 头中包含 『Expires』头信息,启用压缩
  • no-cache:如果 header 头中包含 『Cache-Control:no-cache』头信息,启用压缩
  • no-store:如果 header 头中包含 『Cache-Control:no-store』头信息,启用压缩
  • private:如果 header 头中包含 『Cache-Control:private』头信息,启用压缩
  • no_last_modified:如果 header 头中不包含 『Last-Modified』头信息,启用压缩
  • no_etag:如果 header 头中不包含 『ETag』 头信息,启用压缩
  • auth:如果 header 头中包含 『Authorization』 头信息,启用压缩
  • any:无条件启用压缩

Gzip压缩功能配置模板

1
2
3
4
5
6
7
8
9
gzip on;  			   		 # 开启 Gzip 功能
gzip_types *; # 压缩源文件类型,根据具体的访问资源类型设定
gzip_comp_level 6; # Gzip 压缩级别
gzip_min_length 1k; # 进行压缩响应页面的最小长度,content-length
gzip_buffers 4 16K; # 缓存空间大小
gzip_http_version 1.1; # 指定压缩响应所需要的最低 HTTP 请求版本
gzip_vary on; # 往头信息中添加压缩标识
gzip_disable "MSIE [1-6]\."; # 对 IE6 以下的版本都不进行压缩
gzip_proxied off; # Nginx 作为反向代理压缩服务端返回数据的条件

这些配置在很多地方可能都会用到,所以我们可以将这些内容抽取到一个配置文件中,然后通过 include 指令把配置文件再次加载到 nginx.conf 配置文件中,方法使用。

创建压缩配置文件:nginx_gzip.conf,添加如下内容:

1
2
3
4
5
6
7
8
9
gzip on;
gzip_types *;
gzip_comp_level 6;
gzip_min_length 1k;
gzip_buffers 4 16K;
gzip_http_version 1.1;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
gzip_proxied off;

在 Nginx 核心配置文件 nginx.conf 进行引入,添加如下内容:

1
include nginx_gzip.conf

Gzip和sendfile共存问题

前面在讲解 sendfile 的时候,提到过,开启 sendfile 以后,在读取磁盘上的静态资源文件的时候,可以减少拷贝的次数,可以不经过用户进程将静态文件通过网络设备发送出去,但是 Gzip 要想对资源压缩,是需要经过用户进程进行操作的。Nginx 设置了Gzip 之后,静态文件的 sendfile 就不起作用了。所以要考虑如何解决两个设置的共存问题。

可以使用 ngx_http_gzip_static_module 模块的 gzip_static 指令来解决。

gzip_static指令

gzip_static 指令用于在检查与访问资源同名的 .gz 文件时,response 中以 Gzip 相关的 header 返回 .gz 文件的内容。默认关闭。

语法 默认值 位置
gzip_static <on | off | always>; gzip_static off; http、server、location
1
gzip_static on;

在配置文件添加上述命令后,会报一个错误:unknown directive "gzip_static",主要的原因是 Nginx 默认是没有添加 ngx_http_gzip_static_module 模块。如何来添加?

Nginx模块添加

  1. 查询当前 Nginx 的配置参数,即查看 configure arguments 的配置信息,拷贝出来
1
2
3
nginx -V

# 拷贝 configure arguments 后面的数据
1
--prefix=/usr/local/nginx
  1. 将 Nginx 安装目录下 sbin 目录中的 nginx 二进制文件进行更名备份
1
2
cd /usr/local/nginx/sbin
mv nginx nginx.backup
  1. 进入 Nginx 的安装目录
1
cd /root/nginx/core/nginx-1.21.6
  1. 执行 make clean 清空之前编译的内容
1
make clean
  1. 使用 configure 来配置参数,添加 ngx_http_gzip_static_module 模块,记得加上第1步拷贝的配置信息
1
./configure --with-http_gzip_static_module  # 记得添加 configure arguments 后的数据
  1. 使用 make 命令进行编译
1
make
  1. 将 objs 目录下的 nginx 二进制执行文件移动到 nginx 安装目录下的 sbin 目录中
1
mv /opt/nginx/core/nginx-1.21.6/objs/nginx /usr/local/nginx/sbin

如果不执行第(2)步进行备份,则该步骤会覆盖原来的 nginx 可执行文件

  1. 在源码目录下执行更新命令
1
2
cd /opt/nginx/core/nginx-1.21.6
make upgrade

gzip_static测试

准备好一个 jquery.js 文件,放在 html 目录下

image

  1. 直接访问 http://192.168.91.200/jquery.js

image

  1. 使用 Gzip 命令进行压缩
1
2
3
4
5
# 进入 html 目录
cd /usr/local/nginx/html

# 压缩 js 文件
gzip jquery.js
  1. 再次访问 http://192.168.91.200/jquery.js

image

可以看出 Content-Length 的大小已经变得非常小。

静态资源缓存配置

当浏览器请求 Nginx 服务器的资源后,我们可以让这些资源缓存在浏览器里,这样再一次请求相同的资源时,无需请求 Nginx 服务器,直接从浏览器的缓存里获取,减少 Nginx 服务器的压力。

什么是缓存和Web缓存

缓存(cache),原始意义是指访问速度比一般随机存取存储器(RAM)快的一种高速存储器,通常它不像系统主存那样使用 DRAM 技术,而使用昂贵但较快速的 SRAM 技术。缓存的设置是所有现代计算机系统发挥高性能的重要因素之一。

Web 缓存是指一个 Web 资源(如 html 页面,图片,js,数据等)存在于 Web 服务器和客户端(浏览器)之间的副本。缓存会根据进来的请求保存输出内容的副本;当下一个请求来到的时候,如果是相同的 URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。比较常见的就是浏览器会缓存访问过网站的网页,当再次访问这个 URL 地址的时候,如果网页没有更新,就不会再次下载网页,而是直接使用本地缓存的网页。只有当网站明确标识资源已经更新,浏览器才会再次下载网页。

Web缓存的种类

客户端缓存

  • 浏览器缓存

服务端缓存

  • Nginx
  • Redis
  • Memcached 等

为什么要用浏览器缓存

  • 成本最低的一种缓存实现
  • 减少网络带宽消耗
  • 降低服务器压力
  • 减少网络延迟,加快页面打开速度

浏览器缓存执行流程

HTTP 协议中和页面缓存相关的字段,我们先来认识下:

header 说明
Expires 缓存过期的日期和时间
Cache-Control 设置和缓存相关的配置信息
Last-Modified 请求资源最后修改时间
ETag 请求变量的实体标签的当前值,比如文件的 MD5 值

image

  1. 用户首次通过浏览器发送请求到服务端获取数据,客户端是没有对应的缓存,所以需要发送 request 请求来获取数据;
  2. 服务端接收到请求后,获取服务端的数据及服务端缓存的允许后,返回 200 的成功状态码并且在响应头上附上对应资源以及缓存信息;
  3. 当用户再次访问相同资源的时候,客户端会在浏览器的缓存目录中查找是否存在响应的缓存文件;
  4. 如果没有找到对应的缓存文件,则走第2步;
  5. 如果有缓存文件,接下来对缓存文件是否过期进行判断,过期的判断标准是(Expires);
  6. 如果没有过期,则直接从本地缓存中返回数据进行展示(强缓存);
  7. 如果 Expires 过期,接下来需要判断缓存文件是否发生过变化;
  8. 判断的标准有两个(都要判断),一个是 ETag(Entity Tag),一个是 Last-Modified;
  9. 判断结果是未发生变化,则服务端返回 304,直接从缓存文件中获取数据(弱缓存);
  10. 如果判断是发生了变化,重新从服务端获取数据,并根据缓存协商(服务端所设置的是否需要进行缓存数据的设置)来进行数据缓存。

浏览器缓存相关指令

Nginx 需要进行缓存相关设置,就需要用到如下的指令。

expires指令

该指令用来控制页面缓存的作用。可以通过该指令控制 HTTP 应答中的『Expires』和『Cache-Control』

语法 默认值 位置
expires [modified] <time>; expires <epoch | max | off>; expires off; http、server、location
  • time:可以整数也可以是负数,指定过期时间,单位为 s(秒)。如果是负数,Cache-Control 则为 no-cache,如果为整数或 0,则 Cache-Control 的值为 max-age=time

  • epoch:指定 Expires 的值为『’1 January,1970,00:00:01 GMT’』,即 1970-01-01 00:00:00 ,Cache-Control 的值 no-cache

    因为 Expires(缓存过期时间)是 1970 年,所以不缓存。

  • max:指定 Expires 的值为『’31 December2037 23:59:59GMT’ 』,即(2037-12-31 23:59:59,Cache-Control 的值为 10 年

    因为 Expires(缓存过期时间)是 2037 年,虽然还有 16 年过期,但是最大只能缓存 10 年。

  • off:默认不缓存

例子 1

在配置文件添加如下内容:

1
2
3
4
5
location ~ .*\.(html|js|css|png|jpg|jpeg|gif)$ {
# ...
expires max
# ...
}

发送请求:http://192.168.91.200/jquery.js

查看开发者工具(F12)的 NetWork,如图:

image

315360000 折算下来正好是 10 年。

其他格式

1
2
3
4
5
6
7
expires 30s;  # 表示把数据缓存 30 秒

expires 30m; # 表示把数据缓存 30 分

expires 10h; # 表示把数据缓存 10 小时

expires 1d; # 表示把数据缓存 1 天

add_header指令

add_header 指令是用来添加指定的响应头和响应值。

add_header 是响应体的指令,不是请求时的指令(比如 expires ),并且 add_header 也有和 expires 一样的功能。

如果 expires 和 add_header 同时开启的情况下,则 add_header 优于 expires 生效。

语法 默认值 位置
add_header <name> <value> [always]; location > server > http > ……

always 可选,代表总是添加。

Cache-Control 作为响应头信息,可以在 Nginx 配置文件设置如下缓存响应指令:

1
2
3
4
5
6
7
8
9
add_header Cache-control must-revalidate;
add_header Cache-control no-cache;
add_header Cache-control no-store;
add_header Cache-control no-transform;
add_header Cache-control public;
add_header Cache-control private;
add_header Cache-control proxy-revalidate;
add_header Cache-Control max-age=<seconds>; # 秒
add_header Cache-control s-maxage=<seconds>; # 秒

描述:

指令 说明
must-revalidate 可缓存但必须再向源服务器进行确认
no-cache 数据内容不能被缓存,每次请求都重新访问服务器,若有 max-age,则缓存期间不访问服务器
no-store 不缓存请求或响应的任何内容,暂存也不可以(临时文件夹中不能暂存该资源)
no-transform 代理不可更改媒体类型
public 可以被任何缓存区缓存,如: 浏览器、服务器、代理服务器等
private(默认) 只能在浏览器中缓存,只有在第一次请求的时候才访问服务器,若有 max-age,则缓存期间不访问服务器
proxy-revalidate 要求中间缓存服务器对缓存的响应有效性再进行确认
max-age=<秒> 过期时间,即以秒为单位的缓存时间
s-maxage=<秒> 公共缓存服务器响应的最大 Age 值

值得注意的是:

  • 设置了 no-cache 或者 private 后,打开新窗口时会重新访问服务器。若设置 max-age,则缓存期间不访问服务器
  • 设置 private 和正数的 max-age 时,后退时候不会访问服务器
  • 设置 no-cache 和正数的 max-age 时,后退时会访问服务器

Nginx服务端缓存

在 Web 缓存的种类,我们提到了 Ngixn 服务端缓存,而上面仅仅介绍了在浏览器进行缓存,而因为 Ngixn 服务端缓存的内容比较多,所以前往 Nginx - 缓存集成 进行学习。


Nginx基本使用

Nginx 基本使用

image

目录结构

进入Nginx的主目录我们可以看到这些文件夹

1
2
3
4
5
6
7
8
9
10
11
12
[root@master /]# cd /usr/local/nginx/
[root@master nginx]# ll
总用量 0
drwx------ 2 nobody root 6 7月 27 20:35 client_body_temp
drwxr-xr-x 2 root root 333 7月 27 20:31 conf
drwx------ 2 nobody root 6 7月 27 20:35 fastcgi_temp
drwxr-xr-x 2 root root 40 7月 27 20:31 html
drwxr-xr-x 2 root root 58 7月 27 20:35 logs
drwx------ 2 nobody root 6 7月 27 20:35 proxy_temp
drwxr-xr-x 2 root root 36 7月 27 20:33 sbin
drwx------ 2 nobody root 6 7月 27 20:35 scgi_temp
drwx------ 2 nobody root 6 7月 27 20:35 uwsgi_temp

其中这几个文件夹在刚安装后是没有的,主要用来存放运行过程中的临时文件

1
client_body_temp fastcgi_temp proxy_temp scgi_temp
  • 主要目录说明
目录 说明
conf 用来存放配置文件相关
html 用来存放静态文件的默认目录 html、css等
sbin nginx的主程序

基本运行原理

image

启动停止命令

对于 Nginx 的启停在 Linux 系统中也有很多种方式,我们介绍两种方式:

  • Nginx 服务的信号控制
  • Nginx 的命令行控制

服务信号控制

在了解内容之前,我们首先要考虑一些问题:

  • Nginx 中的 master 和 worker 进程?

  • Nginx 的工作方式?

  • 如何获取进程的 PID?

  • 信号有哪些?

  • 如何通过信号控制 Nginx 的启停等相关操作?

前面在提到 Nginx 的高性能,其实也和它的架构模式有关。Nginx 默认采用的是多进程的方式来工作的,当将 Nginx 启动后,我们通过 ps -ef | grep nginx 命令可以查看到如下内容:

1
ps -ef | grep nginx
1
2
3
4
[root@master sbin]# ps -ef| grep nginx
root 3564 1 0 13:54 ? 00:00:00 nginx: master process ./nginx
nobody 3565 3564 0 13:54 ? 00:00:00 nginx: worker process
root 3567 3483 0 13:54 pts/1 00:00:00 grep --color=auto nginx

从上图中可以看到,Nginx 后台进程中包含一个 master 进程和多个 worker 进程,master 进程主要用来管理 worker 进程,包含接收外界的信息,并将接收到的信号发送给各个 worker 进程,监控 worker 进程的状态。当 worker 进程出现异常退出后,会自动重新启动新的 worker 进程。而 worker 进程则是专门用来处理用户请求的,各个 worker 进程之间是平等的并且相互独立,处理请求的机会也是一样的。

Nginx 的进程模型,我们可以通过下图来说明下:

image

我们现在作为管理员,只需要通过给 master 进程发送信号就可以来控制 Nginx,这个时候我们需要有两个前提条件,一个是要操作的 master 进程,一个是 给 master 进程的信号。

  1. 要想操作 Nginx 的 master 进程,就需要获取到 master 进程的进程号 PID。获取方式简单介绍两个:
  • 通过 ps -ef | grep nginx
1
ps -ef | grep nginx
  • 在讲解 Nginx 的 ./configure 的配置参数的时候,有一个参数 --pid-path=PATH,它的默认值是 /usr/local/nginx/logs/nginx.pid,所以可以通过查看该文件来获取 Nginx 的 master 进程 PID
1
cat /usr/local/nginx/logs/nginx.pid
  1. 信号(signal)
信号 作用
TERM/INT 立即关闭整个服务(关闭 Nginx)
QUIT 「优雅」的关闭整个服务(关闭 Nginx)
HUP 重读配置文件并使用服务对新配置项生效(重启 Nginx)
USR1 重新打开日志文件,可以用来进行日志切割(重启日志)
USR2 平滑升级到最新版的 Nginx
WINCH 所有子进程不在接收处理新连接,相当于给 Work 进程发送 QUIT 指令

调用命令为 kill -signal PID

signal:即为信号;PID 即为获取到的 master 进程 PID

1
kill -signal PID
1
2
3
4
5
6
7
8
9
10
11
# 格式一:
kill -TERM PID

# 立即关闭当前线程
kill -TERM `cat /usr/local/nginx/logs/nginx.pid`

# 格式一:
kill -INT PID

# 立即关闭当前线程
kill -INT `cat /usr/local/nginx/logs/nginx.pid`
  • 案例
  1. 发送 TERM/INT 信号给 master 进程,会将 Nginx 服务立即关闭。
1
2
3
4
5
[root@master nginx]# cat logs/nginx.pid
3564
[root@master nginx]# kill -TERM 3564
[root@master nginx]# ps -ef | grep nginx
root 8874 8753 0 15:58 pts/2 00:00:00 grep --color=auto nginx
  1. 发送 QUIT 信号给 master 进程,master 进程会控制所有的 work 进程不再接收新的请求,等所有请求处理完后,在把进程都关闭掉。
1
2
3
4
5
# 优雅 关闭线程
kill -QUIT PID

# 「优雅」关闭当前线程
kill -QUIT `cat /usr/local/nginx/logs/nginx.pid`
  1. 发送 HUP 信号给 master 进程,master 进程会把控制旧的 worker 进程不再接收新的请求,等处理完请求后将旧的 worker 进程关闭掉,然后根据更改Nginx 的配置文件重新启动新的 worker 进程
1
2
3
4
5
# 重启 worker 进程
kill -HUP PID

# 重启当前 worker 进程
kill -HUP `cat /usr/local/nginx/logs/nginx.pid`
  1. 发送 USR1 信号给 master 进程,告诉 Nginx 重新开启日志文件。如果日志文件被删除了,可以利用此命令重新打开。
1
2
3
4
5
# 重新打开日志文件
kill -USR1 PID

# 重新打开当前 Nginx 的日志文件
kill -USR1 `cat /usr/local/nginx/logs/nginx.pid`
  1. 发送 USR2 信号给 master 进程,告诉 master 进程要平滑升级,这个时候,会重新开启对应的 master 进程和 worker 进程,整个系统中将会有两个master 进程,并且新的 master 进程的 PID 会被记录在 /usr/local/nginx/logs/nginx.pid,而之前的旧的 master 进程 PID 会被记录在 /usr/local/nginx/logs/nginx.pid.oldbin 文件中,接着再次发送 QUIT 信号给旧的 master 进程,让其处理完请求后再进行关闭
1
2
3
4
5
# 开启新的进程,但是不删除旧的进程
kill -USR2 PID

# 开启新的进程,但是不删除当前进程
kill -USR2 `cat /usr/local/nginx/logs/nginx.pid`

当新进程升级后(完全启动后),再关闭旧的进程,旧进程的 PID 在另一个 nginx.pid.oldbin 文件里

1
2
# 关闭旧的线程
kill -QUIT `cat /usr/local/nginx/logs/nginx.pid.oldbin`

image

  1. 发送 WINCH 信号给 master 进程,让 master 进程控制不让所有的 worker 进程在接收新的请求了,请求处理完后关闭 worker 进程。注意 master 进程不会被关闭掉
1
2
3
4
5
# 停止 worker 进程,但是不停止 master 进程
kill -WINCH PID

# 停止当前 worker 进程,但是不停止 master 进程
kill -WINCH `cat /usr/local/nginx/logs/nginx.pid`

命令行控制

此方式是通过 Nginx 安装目录下的 sbin 下的可执行文件 nginx(文件名) 来进行对 Nginx 状态的控制,我们可以通过 nginx -h 来查看都有哪些参数可以用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
[root@master sbin]# ./nginx -h
nginx version: nginx/1.21.6
Usage: nginx [-?hvVtTq] [-s signal] [-p prefix]
[-e filename] [-c filename] [-g directives]

Options:
-?,-h : this help
-v : show version and exit
-V : show version and configure options then exit
-t : test configuration and exit
-T : test configuration, dump it and exit
-q : suppress non-error messages during configuration testing
-s signal : send signal to a master process: stop, quit, reopen, reload
-p prefix : set prefix path (default: /usr/local/nginx/)
-e filename : set error log file (default: logs/error.log)
-c filename : set configuration file (default: conf/nginx.conf)
-g directives : set global directives out of configuration file
选项 作用
-? 和 -h 显示帮助信息
-v 打印版本号信息并退出
-V 打印版本号信息和配置信息并退出
-t 测试 Nginx 的配置文件语法是否正确并退出
-T 测试 Nginx 的配置文件语法是否正确并列出用到的配置文件信息然后退出
-q 在配置测试期间过滤掉非错误消息
-s signal 信号,后面的命令和服务信号控制功能类似: stop:快速关闭,类似于 TERM/INT 信号的作用 quit:优雅的关闭,类似于 QUIT 信号的作用 reopen:重新打开日志文件类似于 USR1 信号的作用 reload:重启 Nginx,类似于 HUP 信号的作用
-p prefix,指定 Nginx 的默认安装路径,(默认为:/usr/local/nginx/)
-c filename,指定 Nginx 的配置文件路径,(默认为:conf/nginx.conf)
-g 用来补充 Nginx 配置文件,向 Nginx 服务指定启动时应用全局的配置
  • 案例

如果觉得每次执行 nginx 指令都必须进入 sbin 目录,则将该指令设置为全局使用。

  1. 两个查看版本命令
1
2
3
4
5
6
7
8
9
10
11
# 查看版本指令 1
[root@master sbin]# nginx -v
# 返回结果
nginx version: nginx/1.21.6

# 查看版本指令 2
[root@master sbin]# nginx -V
# 返回结果
nginx version: nginx/1.21.6
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-44) (GCC)
configure arguments: --prefix=/usr/local/nginx
  1. 测试 Nginx 的配置文件语法

我们首先要知道配置文件的路径在哪,先执行 -t 进行测试

1
2
3
4
5
6
# 测试 Nginx 的配置文件语法
[root@master sbin]# nginx -t

#结果 返回 成功
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful

由第 6 行代码可以知道测试成功,第 5 行代码告诉我们配置文件的目录,我们去修改配置文件,然后再进行测试

1
2
3
4
[root@master sbin]# vim /usr/local/nginx/conf/nginx.conf

###add
HelloWorld

重新进行测试

1
2
3
4
5
6
# 测试 Nginx 的配置文件语法
[root@master sbin]# nginx -t

# 返回结果(失败)
nginx: [emerg] unknown directive "HelloWorld" in /usr/local/nginx/conf/nginx.conf:3
nginx: configuration file /usr/local/nginx/conf/nginx.conf test failed

由第 6 行代码可以知道,配置文件出错了。验证完了,记得将配置文件改回来。

  1. 指定 Nginx 的默认安装路径
1
nginx -p /usr/local/nginx/
  1. 指定 Nginx 的配置文件路径

先把配置文件拷贝到另一个目录,然后修改拷贝后的配置文件内容

1
2
3
4
5
6
7
8
# 拷贝配置文件
cp /usr/local/nginx/conf/nginx.conf /opt

# 修改拷贝后的配置文件内容
vim /opt/nginx.conf

# add
HelloWorld

测试配置文件的时候,指定拷贝后的配置文件进行测试

1
2
3
4
5
6
# 指定配置文件进行测试
[root@master nginx]# nginx -tc /opt/nginx.conf

#返回结果
nginx: [emerg] unknown directive "HelloWorld" in /opt/nginx.conf:3
nginx: configuration file /opt/nginx.conf test failed

说明指定配置文件目录生效,只是文件内容语法不对。

版本升级和新增模块

如果想对 Nginx 的版本进行更新,或者要应用一些新的模块,最简单的做法就是停止当前的 Nginx 服务,然后开启新的 Nginx 服务。但是这样会导致在一段时间内,用户是无法访问服务器。为了解决这个问题,我们就需要用到 Nginx 服务器提供的平滑升级功能。这个也是 Nginx 的一大特点,使用这种方式,就可以使 Nginx 在 7 * 24 小时不间断的提供服务了。接下来我们分析下需求:

需求:Nginx 的版本最开始使用的是 Nginx-1.14.2,由于服务升级,需要将 Nginx 的版本升级到 Nginx-1.16.1,要求 Nginx 不能中断提供服务。

为了应对上述的需求,这里我们提供两种解决方案:

  • 使用 Nginx 服务信号完成 Nginx 的升级
  • 使用 Nginx 安装目录的 make 命令完成升级

版本升级其实就是替换可执行文件 nginx。

环境准备

  1. 先准备两个版本的 Nginx 分别是 1.14.2 和 1.16.1
  2. 使用 Nginx 源码安装的方式将 1.14.2 版本安装成功并正确访问
1
2
3
4
5
6
7
8
9
10
# 解压 1.14.2 版本
tar -xzf nginx-1.14.2.tar.gz
# 进入解压目录
cd nginx-1.14.2/

# 执行配置文件
./configure

# 编译安装
make && make install
  1. 将 Nginx 1.16.1 版本进行参数配置和编译,不需要进行安装。
1
2
3
4
5
6
7
8
9
10
# 解压 1.16.1 版本
tar -xzf nginx-1.16.1.tar.gz
# 进入解压目录
cd nginx-1.16.1/

# 执行配置文件
./configure

# 仅仅编译
make

服务信号进行升级

第一步:将 1.14.2 版本的 sbin 目录下的 nginx 进行备份

不是复制一份,是直接修改原来的 nginx。

1
2
3
4
5
# 进入 sbin 目录下
cd /usr/local/nginx/sbin

# 备份为 nginxold 文件
mv nginx nginx.backup

第二步:将 Nginx 1.16.1 安装目录编译后的 objs 目录下的 nginx 文件,拷贝到原来 /usr/local/nginx/sbin 目录下

如果第一步没有备份,那么将会覆盖 1.14.2 的 nginx 文件

1
2
3
4
5
# 进入 objs 目录
cd ~/nginx/core/nginx-1.16.1/objs

# 拷贝可执行文件到原来的目录
cp nginx /usr/local/nginx/sbin

第三步:发送信号 USR2 给 Nginx 的 1.14.2 版本对应的 master 进程

1
kill -USR2 `cat /usr/local/logs/nginx.pid`

第四步:发送信号 QUIT 给 Nginx 的 1.14.2 版本对应的 master 进程

1
kill -QUIT `cat /usr/local/logs/nginx.pid.oldbin`

安装目录的make命令完成升级

第一步:将 1.14.2 版本的 sbin 目录下的 nginx 进行备份

不是复制一份,是直接修改原来的 nginx。

1
2
3
4
5
# 进入 sbin 目录下
cd /usr/local/nginx/sbin

# 备份为 nginxold 文件
mv nginx nginx.backup

第二步:将 Nginx1.16.1 安装目录编译后的 objs 目录下的 nginx 文件,拷贝到原来 /usr/local/nginx/sbin 目录下

1
2
3
4
5
# 进入 objs 目录
cd ~/nginx/core/nginx-1.16.1/objs

# 拷贝可执行文件到原来的目录
cp nginx /usr/local/nginx/sbin

第三步:进入到安装目录,执行 make upgrade

1
make upgrade

image

第四步:查看是否更新成功

1
nginx -v

在整个过程中,其实 Nginx 是一直对外提供服务的。并且当 Nginx 的服务器启动成功后,我们是可以通过浏览器进行直接访问的,同时我们可以通过更改 html 目录下的页面来修改我们在页面上所看到的内容,那么问题来了,为什么我们要修改 html 目录下的文件,能不能多添加一些页面是 Nginx 的功能更加丰富,还有前面聊到 Nginx 的前端功能又是如何来实现的,这就需要我们对 Nginx 的核心配置文件 进行一个详细的学习。