Nginx
1、Nginx 介绍
传统的 Web 服务器,每个客户端连接作为一个单独的进程或线程处理,需在切换任务时将 CPU 切换到新的任务并创建一个新的运行时上下文,消耗额外的内存和 CPU 时间,当并发请求增加时,服务器响应变慢,从而对性能产生负面影响。
- 静态资源服务,通过本地文件系统提供服务;
- 反向代理服务,延伸出包括缓存、负载均衡等;
- API 服务,OpenResty ;
2、相关概念
2.1 简单请求和非简单请求
首先了解一下简单请求和非简单请求,如果同时满足下面两个条件,就属于简单请求:- 请求方法是
<font style="color:#000000;">HEAD</font>、<font style="color:#000000;">GET</font>、<font style="color:#000000;">POST</font>三种之一; - HTTP 头信息不超过右边着几个字段:
<font style="color:#000000;">Accept</font>、<font style="color:#000000;">Accept-Language</font>、<font style="color:#000000;">Content-Language</font>、<font style="color:#000000;">Last-Event-ID</font>``<font style="color:#000000;">Content-Type</font>只限于三个值<font style="color:#000000;">application/x-www-form-urlencoded</font>、<font style="color:#000000;">multipart/form-data</font>、<font style="color:#000000;">text/plain</font>;
简单请求
对于简单请求,浏览器会在头信息中增加<font style="color:#000000;">Origin</font> 字段后直接发出,<font style="color:#000000;">Origin</font> 字段用来说明,本次请求来自的哪个源(协议+域名+端口)。
如果服务器发现 <font style="color:#000000;">Origin</font> 指定的源不在许可范围内,服务器会返回一个正常的 HTTP 回应,浏览器取到回应之后发现回应的头信息中没有包含 <font style="color:#000000;">Access-Control-Allow-Origin</font> 字段,就抛出一个错误给 XHR 的 <font style="color:#000000;">error</font> 事件;
如果服务器发现 <font style="color:#000000;">Origin</font> 指定的域名在许可范围内,服务器返回的响应会多出几个 <font style="color:#000000;">Access-Control-</font> 开头的头信息字段。
非简单请求
非简单请求是那种对服务器有特殊要求的请求,比如请求方法是<font style="color:#000000;">PUT</font> 或 <font style="color:#000000;">DELETE</font>,或 <font style="color:#000000;">Content-Type</font> 值为 <font style="color:#000000;">application/json</font>。浏览器会在正式通信之前,发送一次 HTTP 预检 <font style="color:#000000;">OPTIONS</font> 请求,先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些 HTTP 请求方法和头信息字段。只有得到肯定答复,浏览器才会发出正式的 <font style="color:#000000;">XHR</font> 请求,否则报错。
2.2 跨域
在浏览器上当前访问的网站向另一个网站发送请求获取数据的过程就是跨域请求。 跨域是浏览器的同源策略决定的,是一个重要的浏览器安全策略,用于限制一个 origin 的文档或者它加载的脚本与另一个源的资源进行交互,它能够帮助阻隔恶意文档,减少可能被攻击的媒介,可以使用 CORS 配置解除这个限制。 这里就列举几个同源和不同源的例子。
# 同源的例子http://example.com/app1/index.html # 只是路径不同http://example.com/app2/index.htmlhttp://Example.com:80 # 只是大小写差异http://example.com# 不同源的例子http://example.com/app1 # 协议不同https://example.com/app2http://example.com # host 不同http://www.example.comhttp://myapp.example.comhttp://example.com # 端口不同http://example.com:8080
2.3 正向代理和反向代理
反向代理(Reverse Proxy)对应的是正向代理(Forward Proxy),他们的区别:正向代理: 一般的访问流程是客户端直接向目标服务器发送请求并获取内容,使用正向代理后,客户端改为向代理服务器发送请求,并指定目标服务器(原始服务器),然后由代理服务器和原始服务器通信,转交请求并获得的内容,再返回给客户端。正向代理隐藏了真实的客户端,为客户端收发请求,使真实客户端对服务器不可见;
举个具体的例子 🌰,浏览器无法直接访问谷歌官网,这时候可以通过一个代理服务器来访问谷歌,那么这个服务器就叫正向代理。反向代理: 与一般访问流程相比,使用反向代理后,直接收到请求的服务器是代理服务器,然后将请求转发给内部网络上真正进行处理的服务器,得到的结果返回给客户端。反向代理隐藏了真实的服务器,为服务器收发请求,使真实服务器对客户端不可见。一般在处理跨域请求的时候比较常用。现在基本上所有的大型网站都设置了反向代理。
举个具体的例子 🌰,去饭店吃饭,可以点川菜、粤菜、江浙菜,饭店也分别有三个菜系的厨师 👨🍳,但是作为顾客不用管哪个厨师做的菜,只用点菜即可,小二将菜单中的菜分配给不同的厨师来具体处理,那么这个小二就是反向代理服务器。 简单的说,一般给客户端做代理的都是正向代理,给服务器做代理的就是反向代理。 正向代理和反向代理主要的原理区别可以参见下图:
2.4 负载均衡
一般情况下,客户端发送多个请求到服务器,服务器处理请求,其中一部分可能要操作一些资源比如数据库、静态资源等,服务器处理完毕后,再将结果返回给客户端。 这种模式对于早期的系统来说,功能要求不复杂,且并发请求相对较少的情况下还能胜任,成本也低。随着信息数量不断增长,访问量和数据量飞速增长,以及系统业务复杂度持续增加,这种做法已无法满足要求,并发量特别大时,服务器容易崩。 很明显这是由于服务器性能的瓶颈造成的问题,除了堆机器之外,最重要的做法就是负载均衡。 请求爆发式增长的情况下,单个机器性能再强劲也无法满足要求了,这个时候集群的概念产生了,单个服务器解决不了的问题,可以使用多个服务器,然后将请求分发到各个服务器上,将负载分发到不同的服务器,这就是负载均衡,核心是「分摊压力」。Nginx 实现负载均衡,一般来说指的是将请求转发给服务器集群。 举个具体的例子 🌰,晚高峰乘坐地铁的时候,入站口经常会有地铁工作人员大喇叭“请走 B 口,B 口人少车空….”,这个工作人员的作用就是负载均衡。
2.5 动静分离
为了加快网站的解析速度,可以把动态页面和静态页面由不同的服务器来解析,加快解析速度,降低原来单个服务器的压力。
3、Nginx 快速安装
3.1 安装
先搜索一下Nginx的安装包
yum list | grep nginx

然后在命令行中
yum install nginx
<font style="color:#000000;">nginx -v</font> 就可以看到具体的 Nginx 版本信息,也就安装完成✅了。

3.2 相关文件夹
然后可以使用<font style="color:#000000;">rpm -ql nginx</font> 来查看 Nginx 被安装到了什么地方,有哪些相关目录,其中位于 <font style="color:#000000;">/etc</font> 目录下的主要是配置文件,还有一些文件见下图:

<font style="color:#000000;">/etc/nginx/conf.d/</font>文件夹,进行子配置的配置项存放处,<font style="color:#000000;">/etc/nginx/nginx.conf</font>主配置文件会默认把这个文件夹中所有子配置项都引入;<font style="color:#000000;">/usr/share/nginx/html/</font>文件夹,通常静态文件都放在这个文件夹,也可以根据自己的习惯放其他地方;
3.3 启动Nginx
安装之后开启 Nginx,如果系统开启了防火墙,那么需要设置一下在防火墙中加入需要开放的端口,下面列举几个常用的防火墙操作(没开启的话可以不用管这个):然后设置 Nginx 的开机启动:
systemctl start firewalld # 开启防火墙systemctl stop firewalld # 关闭防火墙systemctl status firewalld # 查看防火墙开启状态,显示running则是正在运行firewall-cmd --reload # 重启防火墙,永久打开端口需要reload一下# 添加开启端口,--permanent表示永久打开,不加是临时打开重启之后失效firewall-cmd --permanent --zone=public --add-port=8888/tcp# 查看防火墙,添加的端口也可以看到firewall-cmd --list-all
启动 Nginx (其他命令后面有详细讲解):
systemctl enable nginx
然后访问公网 IP,这时候就可以看到 Nginx 的欢迎页面了~
systemctl start nginx
<font style="color:#000000;">Welcome to nginx!</font> 👏
3.4 安装 nvm & node & git
# 下载 nvm,或者看官网的步骤 https://github.com/nvm-sh/nvm#install--update-scriptcurl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.3/install.sh | bashsource ~/.bashrc # 安装完毕后,更新配置文件即可使用 nvm 命令nvm ls-remote # 查看远程 node 版本nvm install v12.16.3 # 选一个你要安装的版本安装,这里选择 12.16.3nvm list # 安装完毕查看安装的 node 版本node -v # 查看是否安装好了yum install git # git 安装
4、Nginx 操作常用命令
Nginx 的命令在控制台中输入<font style="color:#000000;">nginx -h</font> 就可以看到完整的命令,这里列举几个常用的命令:
nginx -s reload # 向主进程发送信号,重新加载配置文件,热重启nginx -s reopen # 重启 Nginxnginx -s stop # 快速关闭nginx -s quit # 等待工作进程处理完成后关闭nginx -T # 查看当前 Nginx 最终的配置nginx -t -c <配置路径> # 检查配置是否有问题,如果已经在配置目录,则不需要-c
<font style="color:#000000;">systemctl</font> 是 Linux 系统应用管理工具 <font style="color:#000000;">systemd</font> 的主命令,用于管理系统,也可以用它来对 Nginx 进行管理,相关命令如下:
systemctl start nginx # 启动 Nginxsystemctl stop nginx # 停止 Nginxsystemctl restart nginx # 重启 Nginxsystemctl reload nginx # 重新加载 Nginx,用于修改配置后systemctl enable nginx # 设置开机启动 Nginxsystemctl disable nginx # 关闭开机启动 Nginxsystemctl status nginx # 查看 Nginx 运行状态
5、Nginx 配置语法
就跟前面文件作用讲解的图所示,Nginx 的主配置文件是<font style="color:#000000;">/etc/nginx/nginx.conf</font>,可以使用 <font style="color:#000000;">cat -n nginx.conf</font> 来查看配置。
<font style="color:#000000;">nginx.conf</font> 结构图可以这样概括:
一个 Nginx 配置文件的结构就像
main # 全局配置,对全局生效├── events # 配置影响 Nginx 服务器或与用户的网络连接├── http # 配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置│ ├── upstream # 配置后端服务器具体地址,负载均衡配置不可或缺的部分│ ├── server # 配置虚拟主机的相关参数,一个 http 块中可以有多个 server 块│ ├── server│ │ ├── location # server 块可以包含多个 location 块,location 指令用于匹配 uri│ │ ├── location│ │ └── ...│ └── ...└── ...
<font style="color:#000000;">nginx.conf</font> 显示的那样,配置文件的语法规则:
- 配置文件由指令与指令块构成;
- 每条指令以
<font style="color:#000000;">;</font>分号结尾,指令与参数间以空格符号分隔; - 指令块以
<font style="color:#000000;">{}</font>大括号将多条指令组织在一起; <font style="color:#000000;">include</font>语句允许组合多个配置文件以提升可维护性;- 使用
<font style="color:#000000;">#</font>符号添加注释,提高可读性; - 使用
<font style="color:#000000;">$</font>符号使用变量; - 部分指令的参数支持正则表达式;
5.1 典型配置
Nginx 的典型配置:server 块可以包含多个 location 块,location 指令用于匹配 uri,语法:
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地址,可以为allallow 172.168.33.44;# 允许访问的ip地址,可以为all}error_page 500 502 503 504 /50x.html; # 默认50x对应的访问页面error_page 400 404 error.html; # 同上}}
指令后面:
location [ = | ~ | ~* | ^~] uri {...}
<font style="color:#000000;">=</font>精确匹配路径,用于不含正则表达式的 uri 前,如果匹配成功,不再进行后续的查找;<font style="color:#000000;">^~</font>用于不含正则表达式的 uri 前,表示如果该符号后面的字符是最佳匹配,采用该规则,不再进行后续的查找;<font style="color:#000000;">~</font>表示用该符号后面的正则去匹配路径,区分大小写;<font style="color:#000000;">~*</font>表示用该符号后面的正则去匹配路径,不区分大小写。跟<font style="color:#000000;">~</font>优先级都比较低,如有多个location的正则能匹配的话,则使用正则表达式最长的那个;
<font style="color:#000000;">~</font> 或 <font style="color:#000000;">~*</font> 标志。
5.2 全局变量
Nginx 有一些常用的全局变量,可以在配置的任何位置使用它们,如下表:| 全局变量名 | 功能 |
|---|---|
$host |
请求信息中的 Host,如果请求中没有 Host行,则等于设置的服务器名,不包含端口 |
$request_method |
客户端请求类型,如 GET、 POST |
$remote_addr |
客户端的 IP地址 |
$args |
请求中的参数 |
$arg_PARAMETER |
GET请求中变量名 PARAMETER 参数的值,例如: $http_user_agent(Uaer-Agent 值), $http_referer… |
$content_length |
请求头中的 Content-length字段 |
$http_user_agent |
客户端agent信息 |
$http_cookie |
客户端cookie信息 |
$remote_addr |
客户端的IP地址 |
$remote_port |
客户端的端口 |
$http_user_agent |
客户端agent信息 |
$server_protocol |
请求使用的协议,如 HTTP/1.0、 HTTP/1.1 |
$server_addr |
服务器地址 |
$server_name |
服务器名称 |
$server_port |
服务器的端口号 |
$scheme |
HTTP 方法(如http,https) |
6、设置二级域名虚拟主机
在某某云 ☁️ 上购买了域名之后,就可以配置虚拟主机了,一般配置的路径在<font style="color:#000000;">域名管理 -> 解析 -> 添加记录</font> 中添加二级域名,配置后某某云会把二级域名也解析到配置的服务器 IP 上,然后在 Nginx 上配置一下虚拟主机的访问监听,就可以拿到从这个二级域名过来的请求了。
image-20200426150644768
<font style="color:#000000;">fe.sherlocked93.club</font> 的时候,也可以访问到服务器了。
由于默认配置文件 <font style="color:#000000;">/etc/nginx/nginx.conf</font> 的 http 模块中有一句 <font style="color:#000000;">include /etc/nginx/conf.d/*.conf</font> 也就是说 <font style="color:#000000;">conf.d</font> 文件夹下的所有 <font style="color:#000000;">*.conf</font> 文件都会作为子配置项被引入配置文件中。为了维护方便,在 <font style="color:#000000;">/etc/nginx/conf.d</font> 文件夹中新建一个 <font style="color:#000000;">fe.sherlocked93.club.conf</font> :
然后在
# /etc/nginx/conf.d/fe.sherlocked93.club.confserver {listen 80;server_name fe.sherlocked93.club;location / {root /usr/share/nginx/html/fe;index index.html;}}
<font style="color:#000000;">/usr/share/nginx/html</font> 文件夹下新建 fe 文件夹,新建文件 <font style="color:#000000;">index.html</font>,内容随便写点,改完 <font style="color:#000000;">nginx -s reload</font> 重新加载,浏览器中输入 <font style="color:#000000;">fe.sherlocked93.club</font>,发现从二级域名就可以访问到刚刚新建的 fe 文件夹。
## 7、配置反向代理
反向代理是工作中最常用的服务器功能,经常被用来解决跨域问题,下面简单介绍一下如何实现反向代理。
首先进入 Nginx 的主配置文件:
为了看起来方便,把行号显示出来
vim /etc/nginx/nginx.conf
<font style="color:#000000;">:set nu</font> (个人习惯),然后去 <font style="color:#000000;">http</font> 模块的 <font style="color:#000000;">server</font> 块中的 <font style="color:#000000;">location /</font>,增加一行将默认网址重定向到最大学习网站 Bilibili 的 <font style="color:#000000;">proxy_pass</font> 配置 🤓 :

<font style="color:#000000;">nginx -s reload</font> 重新加载,进入默认网址,那么现在就直接跳转到 B 站了,实现了一个简单的代理。
实际使用中,可以将请求转发到本机另一个服务器上,也可以根据访问的路径跳转到不同端口的服务中。
比如监听 <font style="color:#000000;">9001</font> 端口,然后把访问不同路径的请求进行反向代理:
- 把访问
[<font style="color:#000000;">http://127.0.0.1:9001/edu</font>](http://127.0.0.1:9001/edu)的请求转发到[<font style="color:#000000;">http://127.0.0.1:8080</font>](http://127.0.0.1:8080) - 把访问
[<font style="color:#000000;">http://127.0.0.1:9001/vod</font>](http://127.0.0.1:9001/vod)的请求转发到[<font style="color:#000000;">http://127.0.0.1:8081</font>](http://127.0.0.1:8081)
反向代理还有一些其他的指令,可以了解一下:
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;}}
<font style="color:#000000;">proxy_set_header</font>:在将客户端请求发送给后端服务器之前,更改来自客户端的请求头信息;<font style="color:#000000;">proxy_connect_timeout</font>:配置 Nginx 与后端代理服务器尝试建立连接的超时时间;<font style="color:#000000;">proxy_read_timeout</font>:配置 Nginx 向后端服务器组发出 read 请求后,等待相应的超时时间;<font style="color:#000000;">proxy_send_timeout</font>:配置 Nginx 向后端服务器组发出 write 请求后,等待相应的超时时间;<font style="color:#000000;">proxy_redirect</font>:用于修改后端服务器返回的响应头中的 Location 和 Refresh。
8、跨域 CORS 配置
关于简单请求、非简单请求、跨域的概念,前面已经介绍过了,还不了解的可以看看前面的讲解。现在前后端分离的项目,经常本地起了前端服务,需要访问不同的后端地址,不可避免遇到跨域问题。

<font style="color:#000000;">fe.sherlocked93.club</font> 和 <font style="color:#000000;">be.sherlocked93.club</font> 二级域名,都指向本云服务器地址,虽然对应 IP 是一样的,但是在 <font style="color:#000000;">fe.sherlocked93.club</font> 域名发出的请求访问 <font style="color:#000000;">be.sherlocked93.club</font> 域名的请求还是跨域了,因为访问的 host 不一致(如果不知道啥原因参见前面跨域的内容)。
8.1 使用反向代理解决跨域
在前端服务地址为<font style="color:#000000;">fe.sherlocked93.club</font> 的页面请求 <font style="color:#000000;">be.sherlocked93.club</font> 的后端服务导致的跨域,可以这样配置:
这样就将对前一个域名
server {listen 9001;server_name fe.sherlocked93.club;location / {proxy_pass be.sherlocked93.club;}}
<font style="color:#000000;">fe.sherlocked93.club</font> 的请求全都代理到了 <font style="color:#000000;">be.sherlocked93.club</font>,前端的请求都被服务器代理到了后端地址下,绕过了跨域。
这里对静态文件的请求和后端服务的请求都以 <font style="color:#000000;">fe.sherlocked93.club</font> 开始,不易区分,所以为了实现对后端服务请求的统一转发,通常会约定对后端服务的请求加上 <font style="color:#000000;">/apis/</font> 前缀或者其他的 path 来和对静态资源的请求加以区分,此时可以这样配置:
这样,静态资源使用
# 请求跨域,约定代理后端服务请求path以/apis/开头location ^~/apis/ {# 这里重写了请求,将正则匹配中的第一个分组的path拼接到真正的请求后面,并用break停止后续匹配rewrite ^/apis/(.*)$ /$1break;proxy_pass be.sherlocked93.club;# 两个域名之间cookie的传递与回写proxy_cookie_domain be.sherlocked93.club fe.sherlocked93.club;}
<font style="color:#000000;">fe.sherlocked93.club/xx.html</font>,动态资源使用 <font style="color:#000000;">fe.sherlocked93.club/apis/getAwo</font>,浏览器页面看起来仍然访问的前端服务器,绕过了浏览器的同源策略,毕竟看起来并没有跨域。
也可以统一一点,直接把前后端服务器地址直接都转发到另一个 <font style="color:#000000;">server.sherlocked93.club</font>,只通过在后面添加的 path 来区分请求的是静态资源还是后端服务,看需求了。
8.2 配置 header 解决跨域
当浏览器在访问跨源的服务器时,也可以在跨域的服务器上直接设置 Nginx,从而前端就可以无感地开发,不用把实际上访问后端的地址改成前端服务的地址,这样可适性更高。 比如前端站点是<font style="color:#000000;">fe.sherlocked93.club</font>,这个地址下的前端页面请求 <font style="color:#000000;">be.sherlocked93.club</font> 下的资源,比如前者的 <font style="color:#000000;">fe.sherlocked93.club/index.html</font> 内容是这样的:
打开浏览器访问
<html><body><h1>welcome fe.sherlocked93.club!!<h1><script type='text/javascript'>var xmlhttp = new XMLHttpRequest()xmlhttp.open("GET", "http://be.sherlocked93.club/index.html", true);xmlhttp.send();</script></body></html>
<font style="color:#000000;">fe.sherlocked93.club/index.html</font> 。
很明显这里是跨域请求,在浏览器中直接访问 [<font style="color:#000000;">http://be.sherlocked93.club/index.html</font>](http://be.sherlocked93.club/index.html) 是可以访问到的,但是在 <font style="color:#000000;">fe.sherlocked93.club</font> 的 html 页面访问就会出现跨域。
在 <font style="color:#000000;">/etc/nginx/conf.d/</font> 文件夹中新建一个配置文件,对应二级域名 <font style="color:#000000;">be.sherlocked93.club</font> :
然后
# /etc/nginx/conf.d/be.sherlocked93.club.confserver {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 可带上 cookieadd_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;}}
<font style="color:#000000;">nginx -s reload</font> 重新加载配置。这时再访问 <font style="color:#000000;">fe.sherlocked93.club/index.html</font> 结果如下,请求中出现了刚刚配置的 Header,解决了跨域问题。
9、开启 gzip 压缩
gzip 是一种常用的网页压缩技术,传输的网页经过 gzip 压缩之后大小通常可以变为原来的一半甚至更小(官网原话),更小的网页体积也就意味着带宽的节约和传输速度的提升,特别是对于访问量巨大大型网站来说,每一个静态资源体积的减小,都会带来可观的流量与带宽的节省。 百度可以找到很多检测站点来查看目标网页有没有开启 gzip 压缩。9.1 Nginx 配置 gzip
使用 gzip 不仅需要 Nginx 配置,浏览器端也需要配合,需要在请求消息头中包含<font style="color:#000000;">Accept-Encoding: gzip</font>(IE5 之后所有的浏览器都支持了,是现代浏览器的默认设置)。一般在请求 html 和 css 等静态资源的时候,支持的浏览器在 request 请求静态资源的时候,会加上 <font style="color:#000000;">Accept-Encoding: gzip</font> 这个 header,表示自己支持 gzip 的压缩方式,Nginx 在拿到这个请求的时候,如果有相应配置,就会返回经过 gzip 压缩过的文件给浏览器,并在 response 相应的时候加上 <font style="color:#000000;">content-encoding: gzip</font> 来告诉浏览器自己采用的压缩方式(因为浏览器在传给服务器的时候一般还告诉服务器自己支持好几种压缩方式),浏览器拿到压缩的文件后,根据自己的解压方式进行解析。
先来看看 Nginx 怎么进行 gzip 配置,和之前的配置一样,为了方便管理,还是在 <font style="color:#000000;">/etc/nginx/conf.d/</font> 文件夹中新建配置文件 <font style="color:#000000;">gzip.conf</font> :
稍微解释一下:
# /etc/nginx/conf.d/gzip.confgzip on; # 默认off,是否开启gzipgzip_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript;# 上面两个开启基本就能跑起了,下面的愿意折腾就了解一下gzip_static on;gzip_proxied any;gzip_vary on;gzip_comp_level 6;gzip_buffers 16 8k;# gzip_min_length 1k;gzip_http_version 1.1;
- gzip_types:要采用 gzip 压缩的 MIME 文件类型,其中 text/html 被系统强制启用;
- gzip_static:默认 off,该模块启用后,Nginx 首先检查是否存在请求静态文件的 gz 结尾的文件,如果有则直接返回该
<font style="color:#000000;">.gz</font>文件内容; - gzip_proxied:默认 off,nginx做为反向代理时启用,用于设置启用或禁用从代理服务器上收到相应内容 gzip 压缩;
- gzip_vary:用于在响应消息头中添加
<font style="color:#000000;">Vary:Accept-Encoding</font>,使代理服务器根据请求头中的<font style="color:#000000;">Accept-Encoding</font>识别是否启用 gzip 压缩; - gzip_comp_level:gzip 压缩比,压缩级别是 1-9,1 压缩级别最低,9 最高,级别越高压缩率越大,压缩时间越长,建议 4-6;
- gzip_buffers:获取多少内存用于缓存压缩结果,16 8k 表示以 8k*16 为单位获得;
- gzip_min_length:允许压缩的页面最小字节数,页面字节数从header头中的
<font style="color:#000000;">Content-Length</font>中进行获取。默认值是 0,不管页面多大都压缩。建议设置成大于 1k 的字节数,小于 1k 可能会越压越大; - gzip_http_version:默认 1.1,启用 gzip 所需的 HTTP 最低版本;
<font style="color:#000000;">server</font> 或者下面的 <font style="color:#000000;">location</font> 模块中,当然像上面这样写的话就是被 include 到 http 模块中了。
其他更全的配置信息可以查看 <官网文档ngx_http_gzip_module>,配置前是这样的:


<font style="color:#000000;">Content-Encoding: gzip</font>,返回信息被压缩了:


<font style="color:#000000;">gzip_min_length 1k</font>,不加的话:
由于文件太小,gzip 压缩之后得到了 -48% 的体积优化,压缩之后体积还比压缩之前体积大了,所以最好设置低于 <font style="color:#000000;">1kb</font> 的文件就不要 gzip 压缩了。
9.2 Webpack 的 gzip 配置
当前端项目使用 Webpack 进行打包的时候,也可以开启 gzip 压缩:由此打包出来的文件如下图:
// vue-cli3 的 vue.config.js 文件const CompressionWebpackPlugin = require('compression-webpack-plugin')module.exports = {// gzip 配置configureWebpack: config => {if (process.env.NODE_ENV === 'production') {// 生产环境return {plugins: [new CompressionWebpackPlugin({test: /\.js$|\.html$|\.css/, // 匹配文件名threshold: 10240, // 文件压缩阈值,对超过10k的进行压缩deleteOriginalAssets: false// 是否删除源文件})]}}},...}

<font style="color:#000000;">.gz</font> 经过 <font style="color:#000000;">gzip</font> 压缩之后的文件,这是因为这个文件超过了 <font style="color:#000000;">10kb</font>,有的文件没有超过 <font style="color:#000000;">10kb</font> 就没有进行 <font style="color:#000000;">gzip</font> 打包,如果期望压缩文件的体积阈值小一点,可以在 <font style="color:#000000;">compression-webpack-plugin</font> 这个插件的配置里进行对应配置。
那么为啥这里 Nginx 已经有了 gzip 压缩,Webpack 这里又整了个 gzip 呢,因为如果全都是使用 Nginx 来压缩文件,会耗费服务器的计算资源,如果服务器的 <font style="color:#000000;">gzip_comp_level</font> 配置的比较高,就更增加服务器的开销,相应增加客户端的请求时间,得不偿失。
如果压缩的动作在前端打包的时候就做了,把打包之后的高压缩等级文件作为静态资源放在服务器上,Nginx 会优先查找这些压缩之后的文件返回给客户端,相当于把压缩文件的动作从 Nginx 提前给 Webpack 打包的时候完成,节约了服务器资源,所以一般推荐在生产环境应用 Webpack 配置 gzip 压缩。
10、配置负载均衡
负载均衡在之前已经介绍了相关概念了,主要思想就是把负载均匀合理地分发到多个服务器上,实现压力分流的目的。 主要配置如下:Nginx 提供了好几种分配方式,默认为轮询,就是轮流来。有以下几种分配方式:
http {upstream myserver {# ip_hash; # ip_hash 方式# fair; # fair 方式server 127.0.0.1:8081; # 负载均衡目的服务地址server 127.0.0.1:8080;server 127.0.0.1:8082 weight=10; # weight 方式,不写默认为 1}server {location / {proxy_pass http://myserver;proxy_connect_timeout 10;}}}
- 轮询,默认方式,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务挂了,能自动剔除;
- weight,权重分配,指定轮询几率,权重越高,在被访问的概率越大,用于后端服务器性能不均的情况;
- ip_hash,每个请求按访问 IP 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决动态网页 session 共享问题。负载均衡每次请求都会重新定位到服务器集群中的某一个,那么已经登录某一个服务器的用户再重新定位到另一个服务器,其登录信息将会丢失,这样显然是不妥的;
- fair(第三方),按后端服务器的响应时间分配,响应时间短的优先分配,依赖第三方插件 nginx-upstream-fair,需要先安装;
11、配置动静分离
动静分离在之前也介绍过了,就是把动态和静态的请求分开。方式主要有两种,一种 是纯粹把静态文件独立成单独的域名,放在独立的服务器上,也是目前主流推崇的方案。另外一种方法就是动态跟静态文件混合在一起发布, 通过 Nginx 配置来分开。 通过 location 指定不同的后缀名实现不同的请求转发。通过 expires 参数设置,可以使浏览器缓存过期时间,减少与服务器之前的请求和流量。具体 expires 定义:是给一个资源设定一个过期时间,也就是说无需去服务端验证,直接通过浏览器自身确认是否过期即可,所以不会产生额外的流量。此种方法非常适合不经常变动的资源。(如果经常更新的文件,不建议使用 expires 来缓存),这里设置 3d,表示在这 3 天之内访问这个URL,发送一个请求,比对服务器该文件最后更新时间没有变化。则不会从服务器抓取,返回状态码 304,如果有修改,则直接从服务器重新下载,返回状态码 200。
server {location /www/ {root /data/;index index.html index.htm;}location /image/ {root /data/;autoindex on;}}
12、配置高可用集群(双机热备)
当主 Nginx 服务器宕机之后,切换到备份 Nginx 服务器
然后编辑
yum install keepalived -y
<font style="color:#000000;">/etc/keepalived/keepalived.conf</font> 配置文件,并在配置文件中增加 <font style="color:#000000;">vrrp_script</font> 定义一个外围检测机制,并在 <font style="color:#000000;">vrrp_instance</font> 中通过定义 <font style="color:#000000;">track_script</font> 来追踪脚本执行过程,实现节点转移:
其中检测脚本
global_defs{notification_email {acassen@firewall.loc}notification_email_from Alexandre@firewall.locsmtp_server 127.0.0.1smtp_connect_timeout 30// 上面都是邮件配置,没卵用router_id LVS_DEVEL // 当前服务器名字,用hostname命令来查看}vrrp_script chk_maintainace { // 检测机制的脚本名称为chk_maintainacescript "[[ -e/etc/keepalived/down ]] && exit 1 || exit 0"// 可以是脚本路径或脚本命令// script "/etc/keepalived/nginx_check.sh" // 比如这样的脚本路径interval 2 // 每隔2秒检测一次weight -20 // 当脚本执行成立,那么把当前服务器优先级改为-20}vrrp_instanceVI_1 { // 每一个vrrp_instance就是定义一个虚拟路由器state MASTER // 主机为MASTER,备用机为BACKUPinterface eth0 // 网卡名字,可以从ifconfig中查找virtual_router_id 51 // 虚拟路由的id号,一般小于255,主备机id需要一样priority 100 // 优先级,master的优先级比backup的大advert_int 1 // 默认心跳间隔authentication { // 认证机制auth_type PASSauth_pass 1111 // 密码}virtual_ipaddress { // 虚拟地址vip172.16.2.8}}
<font style="color:#000000;">nginx_check.sh</font>,这里提供一个:
复制一份到备份服务器,备份 Nginx 的配置要将
#!/bin/bashA=`ps -C nginx --no-header | wc -l`if [ $A -eq 0 ];then/usr/sbin/nginx # 尝试重新启动nginxsleep 2 # 睡眠2秒if [ `ps -C nginx --no-header | wc -l` -eq 0 ];thenkillall keepalived # 启动失败,将keepalived服务杀死。将vip漂移到其它备份节点fifi
<font style="color:#000000;">state</font> 后改为 <font style="color:#000000;">BACKUP</font>,<font style="color:#000000;">priority</font> 改为比主机小。
设置完毕后各自 <font style="color:#000000;">service keepalived start</font> 启动,经过访问成功之后,可以把 Master 机的 keepalived 停掉,此时 Master 机就不再是主机了 <font style="color:#000000;">service keepalived stop</font>,看访问虚拟 IP 时是否能够自动切换到备机 <font style="color:#000000;">ip addr</font>。
再次启动 Master 的 keepalived,此时 vip 又变到了主机上。
13、适配 PC 或移动设备
根据用户设备不同返回不同样式的站点,以前经常使用的是纯前端的自适应布局,但无论是复杂性和易用性上面还是不如分开编写的好,比如常见的淘宝、京东……这些大型网站就都没有采用自适应,而是用分开制作的方式,根据用户请求的<font style="color:#000000;">user-agent</font> 来判断是返回 PC 还是 H5 站点。
首先在 <font style="color:#000000;">/usr/share/nginx/html</font> 文件夹下 <font style="color:#000000;">mkdir</font> 分别新建两个文件夹 <font style="color:#000000;">PC</font> 和 <font style="color:#000000;">mobile</font>,<font style="color:#000000;">vim</font> 编辑两个 <font style="color:#000000;">index.html</font> 随便写点内容。
然后和设置二级域名虚拟主机时候一样,去
cd /usr/share/nginx/htmlmkdir pc mobilecd pcvim index.html # 随便写点比如 hello pc!cd ../mobilevim index.html # 随便写点比如 hello mobile!
<font style="color:#000000;">/etc/nginx/conf.d</font> 文件夹下新建一个配置文件 <font style="color:#000000;">fe.sherlocked93.club.conf</font> :
配置基本没什么不一样的,主要多了一个
# /etc/nginx/conf.d/fe.sherlocked93.club.confserver {listen 80;server_name fe.sherlocked93.club;location / {root /usr/share/nginx/html/pc;if ($http_user_agent ~* '(Android|webOS|iPhone|iPod|BlackBerry)') {root /usr/share/nginx/html/mobile;}index index.html;}}
<font style="color:#000000;">if</font> 语句,然后使用 <font style="color:#000000;">$http_user_agent</font> 全局变量来判断用户请求的 <font style="color:#000000;">user-agent</font>,指向不同的 root 路径,返回对应站点。
在浏览器访问这个站点,然后 F12 中模拟使用手机访问:

14、配置 HTTPS
一般都会有免费申请的服务器证书,安装直接看所在云的操作指南即可。 腾讯云提供的亚洲诚信机构颁发的免费证书只能一个域名使用,二级域名什么的需要另外申请,但是申请审批比较快,一般几分钟就能成功,然后下载证书的压缩文件,里面有个 nginx 文件夹,把<font style="color:#000000;">xxx.crt</font> 和 <font style="color:#000000;">xxx.key</font> 文件拷贝到服务器目录,再配置下:
写完
server {listen 443 ssl http2 default_server; # SSL 访问端口号为 443server_name sherlocked93.club; # 填写绑定证书的域名ssl_certificate /etc/nginx/https/1_sherlocked93.club_bundle.crt; # 证书文件地址ssl_certificate_key /etc/nginx/https/2_sherlocked93.club.key; # 私钥文件地址ssl_session_timeout 10m;ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #请按照以下协议配置ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;ssl_prefer_server_ciphers on;location / {root /usr/share/nginx/html;index index.html index.htm;}}
<font style="color:#000000;">nginx -t -q</font> 校验一下,没问题就 <font style="color:#000000;">nginx -s reload</font>,现在去访问 [<font style="color:#000000;">https://sherlocked93.club/</font>](https://sherlocked93.club/) 就能访问 HTTPS 版的网站了。
一般还可以加上几个增强安全性的命令:
add_header X-Frame-Options DENY; # 减少点击劫持add_header X-Content-Type-Options nosniff; # 禁止服务器自动解析资源类型add_header X-Xss-Protection 1; # 防XSS攻击
15、一些常用技巧
15.1 静态服务
server {listen 80;server_name static.sherlocked93.club;charset utf-8; # 防止中文文件名乱码location /download {alias /usr/share/nginx/html/static; # 静态资源目录autoindex on; # 开启静态资源列目录autoindex_exact_size off; # on(默认)显示文件的确切大小,单位是byte;off显示文件大概大小,单位KB、MB、GBautoindex_localtime off; # off(默认)时显示的文件时间为GMT时间;on显示的文件时间为服务器时间}}
15.2 图片防盗链
server {listen 80;server_name *.sherlocked93.club;# 图片防盗链location ~* \.(gif|jpg|jpeg|png|bmp|swf)$ {valid_referers none blocked 192.168.0.2; # 只允许本机 IP 外链引用if ($invalid_referer){return 403;}}}
15.3 请求过滤
# 非指定请求全返回 403if ( $request_method !~ ^(GET|POST|HEAD)$ ) {return 403;}location / {# IP访问限制(只允许IP是 192.168.0.2 机器访问)allow 192.168.0.2;deny all;root html;index index.html index.htm;}
15.4 配置图片、字体等静态文件缓存
由于图片、字体、音频、视频等静态文件在打包的时候通常会增加了 hash,所以缓存可以设置的长一点,先设置强制缓存,再设置协商缓存;如果存在没有 hash 值的静态文件,建议不设置强制缓存,仅通过协商缓存判断是否需要使用缓存。
# 图片缓存时间设置location ~ .*\.(css|js|jpg|png|gif|swf|woff|woff2|eot|svg|ttf|otf|mp3|m4a|aac|txt)$ {expires 10d;}# 如果不希望缓存expires -1;
15.5 单页面项目 history 路由配置
server {listen 80;server_name fe.sherlocked93.club;location / {root /usr/share/nginx/html/dist; # vue 打包后的文件夹index index.html index.htm;try_files $uri$uri/ /index.html @rewrites;expires -1; # 首页一般没有强制缓存add_header Cache-Control no-cache;}# 接口转发,如果需要的话#location ~ ^/api {# proxy_pass http://be.sherlocked93.club;#}location @rewrites {rewrite ^(.+)$ /index.html break;}}
15.6 HTTP 请求转发到 HTTPS
配置完 HTTPS 后,浏览器还是可以访问 HTTP 的地址[<font style="color:#000000;">http://sherlocked93.club/</font>](http://sherlocked93.club/) 的,可以做一个 301 跳转,把对应域名的 HTTP 请求重定向到 HTTPS 上
server {listen 80;server_name www.sherlocked93.club;# 单域名重定向if ($host = 'www.sherlocked93.club'){return 301 https://www.sherlocked93.club$request_uri;}# 全局非 https 协议时重定向if ($scheme != 'https') {return 301 https://$server_name$request_uri;}# 或者全部重定向return 301 https://$server_name$request_uri;# 以上配置选择自己需要的即可,不用全部加}
15.7 泛域名路径分离
这是一个非常实用的技能,经常有时候可能需要配置一些二级或者三级域名,希望通过 Nginx 自动指向对应目录,比如:<font style="color:#000000;">test1.doc.sherlocked93.club</font>自动指向<font style="color:#000000;">/usr/share/nginx/html/doc/test1</font>服务器地址;<font style="color:#000000;">test2.doc.sherlocked93.club</font>自动指向<font style="color:#000000;">/usr/share/nginx/html/doc/test2</font>服务器地址;
server {listen 80;server_name ~^([\w-]+)\.doc\.sherlocked93\.club$;root /usr/share/nginx/html/doc/$1;}
15.8 泛域名转发
和之前的功能类似,有时候希望把二级或者三级域名链接重写到希望的路径,让后端就可以根据路由解析不同的规则:<font style="color:#000000;">test1.serv.sherlocked93.club/api?name=a</font>自动转发到<font style="color:#000000;">127.0.0.1:8080/test1/api?name=a </font>;<font style="color:#000000;">test2.serv.sherlocked93.club/api?name=a</font>自动转发到<font style="color:#000000;">127.0.0.1:8080/test2/api?name=a</font>;
server {listen 80;server_name ~^([\w-]+)\.serv\.sherlocked93\.club$;location / {proxy_set_header X-Real-IP $remote_addr;proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;proxy_set_header Host $http_host;proxy_set_header X-NginX-Proxy true;proxy_pass http://127.0.0.1:8080/$1$request_uri;}}
16、最佳实践
- 为了使 Nginx 配置更易于维护,建议为每个服务创建一个单独的配置文件,存储在
<font style="color:#000000;">/etc/nginx/conf.d</font>目录,根据需求可以创建任意多个独立的配置文件。 - 独立的配置文件,建议遵循以下命名约定
<font style="color:#000000;"><服务>.conf</font>,比如域名是<font style="color:#000000;">sherlocked93.club</font>,那么配置文件的应该是这样的<font style="color:#000000;">/etc/nginx/conf.d/sherlocked93.club.conf</font>,如果部署多个服务,也可以在文件名中加上 Nginx 转发的端口号,比如<font style="color:#000000;">sherlocked93.club.8080.conf</font>,如果是二级域名,建议也都加上<font style="color:#000000;">fe.sherlocked93.club.conf</font>。 - 常用的、复用频率比较高的配置可以放到
<font style="color:#000000;">/etc/nginx/snippets</font>文件夹,在 Nginx 的配置文件中需要用到的位置 include 进去,以功能来命名,并在每个 snippet 配置文件的开头注释标明主要功能和引入位置,方便管理。比如之前的<font style="color:#000000;">gzip</font>、<font style="color:#000000;">cors</font>等常用配置,都设置了 snippet。 - Nginx 日志相关目录,内以
<font style="color:#000000;">域名.type.log</font>命名(比如<font style="color:#000000;">be.sherlocked93.club.access.log</font>和<font style="color:#000000;">be.sherlocked93.club.error.log</font>)位于<font style="color:#000000;">/var/log/nginx/</font>目录中,为每个独立的服务配置不同的访问权限和错误日志文件,这样查找错误时,会更加方便快捷。
