前端应该掌握的Nginx相关知识

70 阅读3分钟

Nginx是在前端服务部署时是很重要的一部分,也是部署的基础,学会了通过Nginx部署前端资源,才能继续后续的一系列进阶。

基础知识

现在服务器安装Nginx很简单,一般只需要两行命令即可,安装完成后,启动服务

# 安装nginx  
sudo yum install -y nginx  
# 启动nginx  
sudo systemctl start nginx  
# 查看nginx运行状态  
sudo systemctl status nginx

image.png

当我们看到下图,说明你的Nginx运行正常,此时Nginx会启动服务,默认80端口。此时如果我们的服务器外网防火墙80端口开启,那么访问外网IP,就能看到Nginx启动的服务 image.png

Nginx的配置文件,一般位于/etc/nginx目录下,具体内容如下:

image.png

我们基本只需要关注文件nginx.confconf.d目录,下面是一份nginx.conf配置文件,不懂也不要怕,基本都不需要改动,默认80服务已经开启了。

nginx.conf配置注解

user nginx; # nginx进行运行的用户 
worker_processes auto; # nginx进程数,建议设置为等于CPU总核心数。
error_log /var/log/nginx/error.log; # 错误日志
pid /run/nginx.pid; # 设置pid文件的存放路径

# 事件区块开始
events {
    #单个进程最大连接数(最大连接数=连接数*进程数)
    #根据硬件调整,和前面工作进程配合起来用,尽量大,但是别把cpu跑到100%就行。每个进程允许的最多连接数,理论上每台nginx服务器的最大连接数为。
    worker_connections  1024;
}

#设定http服务器,利用它的反向代理功能提供负载均衡支持
http {
    # nginx日志格式
    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;
    #开启高效文件传输模式,sendfile指令指定nginx是否调用sendfile函数来输出文件,对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。注意:如果图片显示不正常把这个改成off。
    #sendfile指令指定 nginx 是否调用sendfile 函数(zero copy 方式)来输出文件,对于普通应用,必须设为on。如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络IO处理速度,降低系统uptime。
    sendfile on;
    # 禁用Nginx对HTTP响应消息体的分块处理
    tcp_nopush on;
    # 开启Nginx的TCP_NODELAY选项,即禁用Nagle算法
    tcp_nodelay on;
    # 设置了HTTP持久连接(Keep-Alive)的超时时间,单位为秒
    keepalive_timeout 65;
    # 配置了Nginx用于映射文件扩展名到MIME类型的哈希表的最大大小
    types_hash_max_size 2048;
    
    # include:导入外部文件mime.types,将所有types提取为文件,然后导入到nginx配置文件中
    include             /etc/nginx/mime.types;
    # 默认文件类型
    default_type        application/octet-stream;
    
    # 引入的nginx配置文件,可以将server放在该目录下,方便管理
    include /etc/nginx/conf.d/*.conf;
    
    # 第一个Server区块开始,表示一个独立的虚拟主机站点
    server {
        listen 80 default_server; # 服务启动的端口
        listen [::]:80 default_server;
        server_name  _; # 服务域名或IP
        root /usr/share/nginx/html; # 服务指向的文件地址

        # 对 "/" 启用反向代理,第一个location区块开始
        location / {
            root   html;  #服务默认启动目录
            index  index.html index.htm; # 默认的首页文件,多个用空格分开
        }
        
        
         # 错误页面路由
         error_page 404 /404.html; # 找不到资源重定向到404页面  
         location = /40x.html {};  
              
         error_page 500 502 503 504 /50x.html; # 系统错误重定向50x页面  
         location = /50x.html {};
    }
    
    # server {  
    #    listen 443; # 支持https协议  
    #    server_name _;  
    #    root /usr/share/nginx/html;  
    #    ...  
    # }
}

理解

虽然上面有了注解,但是看起来还是很迷糊。接着解释:我们可以把nginx.conf分为三个部分进行理解

  • 第一部分:全局块
  • 第二部分:events块
  • 第三部分:http块

image.png

全局块

作用:从配置文件开始到 events 块之间的内容,主要会设置一些影响nginx 服务器整体运行的配置指令,主要包括配 置运行 Nginx 服务器的用户(组)、允许生成的 worker process 数,进程 PID 存放路径、日志存放路径和类型以 及配置文件的引入等。

比如上面第一行配置的 worker_processes auto;

这是 Nginx 服务器并发处理服务的关键配置,worker_processes 值越大,可以支持的并发处理量也越多,但是 会受到硬件、软件等设备的制约。

events块

作用:events 块涉及的指令主要影响 Nginx 服务器与用户的网络连接,常用的设置包括是否开启对多 work process 下的网络连接进行序列化,是否 允许同时接收多个网络连接,选取哪种事件驱动模型来处理连接请求,每个 word process 可以同时支持的最大连接数等。

比如worker_connections 1024; 就表示每个 work process 支持的最大连接数为 1024。

这部分的配置对 Nginx 的性能影响较大,在实际中应该灵活配置。

http块

作用:这算是 Nginx 服务器配置中最频繁的部分,代理、缓存和日志定义等绝大多数功能和第三方模块的配置都在这里。

需要注意的是:http 块也可以包括 http全局块、server 块。

http全局块 http全局块配置的指令包括文件引入、MIME-TYPE 定义、日志自定义、连接超时时间、单链接请求数上限等。

server 块 这块和虚拟主机有密切关系,虚拟主机从用户角度看,和一台独立的硬件主机是完全一样的,该技术的产生是为了 节省互联网服务器硬件成本。

每个 http 块可以包括多个 server 块,而每个 server 块就相当于一个虚拟主机。 而每个 server 块也分为全局 server 块,以及可以同时包含多个 locaton 块。

全局 server 块 最常见的配置是本虚拟机主机的监听配置和本虚拟主机的名称或IP配置。

location 块 一个 server 块可以配置多个 location 块。

这块的主要作用是基于 Nginx 服务器接收到的请求字符串(例如 server_name/uri-string),对虚拟主机名称 (也可以是IP 别名)之外的字符串(例如 前面的 /uri-string)进行匹配,对特定的请求进行处理。地址定向、数据缓 存和应答控制等功能,还有许多第三方模块的配置也在这里进行。

image.png

Nginx配置

此处我以我的个人项目举例,我是vue3+vite项目,打包为cloud-music,我的前端包直接放在root底下

部署单页面应用

Hash模式

我是希望通过包名访问页面,所以配置如下

server {
    listen       80 default_server;
    listen       [::]:80 default_server;
    server_name  localhost;
    root /root/cloud-music/;

    location / {
        index  index.html index.htm;
    }

    location /cloud-music {
        index  index.html index.htm;
        alias /root/cloud-music;
    }
}

修改完成之后执行nginx -t确认配置文件修改没问题,再执行nginx -s reload重启Nginx,此时我们访问外网IP(默认80端口,下面默认都是访问80端口),可以看到

这里我使用的jenkins持续集成,当我提交代码会自动帮我构建部署,有关jenkins的相关操作请移步前端工程化:Jenkins 实现前端自动化构建部署本文主要用于记录分享Jenkins + Nginx + pm2实现前端 - 掘金 (juejin.cn)

History模式

同样部署在相同目录下时会发现访问非首页的其他页面刷新浏览器会出现404

原因

hash路由模式下,URL中的Hash值(#后面的部分)用来表示应用的状态或路由信息。当用户切换路由时,只有Hash部分发生变化,并没有向服务器发出请求,就做到了浏览器对于页面路由的管理。

  • Hash模式下,URL和路由路径由#号分隔:http://example.com/#/about?query=abc
  • #后面的路径发生变化时,会触发浏览器的hashchange事件,通过hashchange事件监听到路由路径的变化,从而导航到不同的路由页面。
  • Hash模式#后面的路径并不会作为URL出现在网络请求中。例如对于输入的example.com/#/about[1] ,实际上请求的URL是example.com/[2] ,所以不管输入的Hash路由路径是什么,实际网络请求的都是主域名或IP:Port

History路由模式下,调用浏览器HTML5中historyAPI来管理导航。URL和路径是连接在一起的,路由的路径包含在请求的URL里面,路由路径作为URL的一部分一起发送。

  • History模式下,URL路由格式为:http://example.com/about&query=abc
  • 当我们向服务器发出请求时,服务器会请求对应的路径的资源

综上,当我们打开入口文件index.html的路径时,切换url此时是本地路由,访问正常,但是当我们处于非入口页面时,刷新浏览器,此时发出请求,由于服务器就找不到资源路径了,变成了404。

而对于Hash模式来说,总是请求的根路径,所以不会出现这种情况。

解决

修改Nginx配置nginx.conf

server {
    listen       80 default_server;
    listen       [::]:80 default_server;
    server_name  localhost;
    root /root/cloud-music/;

    location / {
        try_files $uri $uri/ /index.html;
        index  index.html index.htm;
    }

    location /cloud-music {
        index  index.html index.htm;
        alias /root/cloud-music;
        try_files $uri $uri/ /root/cloud-music/index.html;
    }
}

重启nginx nginx -tnginx -s reload 再次刷新页面,发现页面访问正常了,切换也没有问题

反向代理

反向代理的用途很多,这里我们看一个常用的,代理请求的接口。我们在发布时前端的域名和后端api服务的域名经常不一致,此时就可以使用Nginx配置反向代理来解决这个问题。

server {  
#接口端
    location /api/ {
        proxy_pass   http://118.89.113.58:3000/; # 此处是我后台接口启动的端口
        proxy_redirect default;
        proxy_http_version 1.1;
        proxy_connect_timeout   60;
        proxy_send_timeout      60;
        proxy_read_timeout      90;
    }
}

Gzip

作为前端性能优化的一种方式,Gzip是简单且有效的,尽管目前前端对于静态资源会进行压缩,但Gzip依然可以在网络传输过程中对文件进行压缩

server {
    gzip on; # 默认off,是否开启gzip
    # 开启gzip匹配文件类型
    gzip_types application/atom+xml application/javascript application/json application/rss+xml application/vnd.ms-fontobject application/x-font-ttf application/x-web-app-manifest+json application/xhtml+xml application/xml font/opentype image/svg+xml image/x-icon text/css text/plain text/javascript text/x-component;
    
    # 上面两个开启基本就能跑起了,下面的愿意折腾就了解一下
    # 启用对静态文件的GZIP压缩
    gzip_static on;
    # 设置在代理模式下是否启用GZIP压缩
    gzip_proxied any;
    # 启用`Vary` HTTP头,告诉下游缓存代理根据`Accept-Encoding`请求头来缓存不同的响应版本
    gzip_vary on; **一定要开启,不开启读取不到.gz结尾的压缩文件**
    # 设置压缩级别,范围从1到9,数字越大压缩率越高但消耗的CPU资源也越多。这里设置为5是一个比较平衡的选择
    gzip_comp_level 5;
    # 定义用于读取客户端请求的缓冲区大小
    gzip_buffers 4 16k;
    # 单位Byte 过小的文件没必要压缩
    gzip_min_length 1000; 
    # 指定HTTP版本
    gzip_http_version 1.0;
}

注意gzip_static on;是开启静态文件的GZIP压缩,这种需要搭配前端打包优化处理,对vite + vue3项目打包的时候进行 gzip 压缩

  1. 安装 vite-plugin-compression 插件
npm install vite-plugin-compression

2. vite.config.ts/js 中导入vite-plugin-compression插件

import viteCompression from 'vite-plugin-compression'

3. 在 vite.config.ts/js 中的 plugins 中使用vite-plugin-compression插件即可

export default defineConfig({
	plugins: [
		viteCompression({
	      filter: /\.(js|css|json|txt|html|ico|svg)(\?.*)?$/i, // 需要压缩的文件
	      threshold: 1024, // 文件容量大于这个值进行压缩
	      algorithm: 'gzip', // 压缩方式
	      ext: 'gz', // 后缀名
	      deleteOriginFile: false, // 压缩后是否删除压缩源文件
    	})
	]
})

一个ip上线多个网站

直接复制server块即可,一个server块就代表了一个网站,需要改端口和文件的路径等内容

# 这里仅举例
# 第一个网站
  server {
   listen       80;
   root         /root/cloud-music/;
   index        index.html;
   location / {
    try_files $uri $uri/ /index.html; # 路由模式history的修改
   }
  }

 # 第二个网站
 server {
  listen       5317;
  root         /root/h5-music;
  index        index.html;
  location / {}
 }

负载均衡

Nginx可以作为负载均衡服务器使用,通过配置upstream来分发流量,同时可以配置一些参数:

  • weight:分发权重
  • ip_hash:配置始终将ip的请求始终转发到同一台后端服务器。
  • max_fails: 将某个后台服务标记为不可用之前,允许请求失败的次数
  • backup:标记当前服务为备用服务
  • down:暂时不可用
upstream api {  
        ip_hash;  
        server backend1.example.com;  
        server backend2.example.com;  
        # server backend1.example.com weight=5;  
    }  
    server {  
        location /api {  
            proxy_pass http://api;  
            proxy_set_header Host $host
            proxy_set_header X-Real-IP $remote_addr
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
        }  
    }

跨域处理

通过反向代理,已经处理了请求域名和端口不一致的跨域问题,但有局限性。Nginx有专门方法配置请求资源的跨域

该字段可以放在server、location指令模块,通过配置头部字段,做跨域处理

server {  
  
    location / {  
        # 允许所有来源的跨域请求  
        add_header Access-Control-Allow-Origin *;  

        # 允许特定的HTTP方法(GET、POST等)  
        add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE";  

        # 允许特定的HTTP请求头字段  
        add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept";  

        # 响应预检请求的最大时间  
        add_header Access-Control-Max-Age 3600;  

        # 允许携带身份凭证(如Cookie)  
        add_header Access-Control-Allow-Credentials true;  

        # 处理 OPTIONS 预检请求  
        if ($request_method = 'OPTIONS') {  
            add_header Access-Control-Allow-Methods "GET, POST, OPTIONS, PUT, DELETE";  
            add_header Access-Control-Allow-Headers "Origin, X-Requested-With, Content-Type, Accept";  
            add_header Access-Control-Max-Age 3600;  
            add_header Access-Control-Allow-Credentials true;  
            add_header Content-Length 0;  
            add_header Content-Type text/plain;  
            return 204;  
        }  
    }  
}