前端狗啥都要学系列

# Nginx 定义 & 优点

Nginx (engine x) 是一个轻量级、高性能的 HTTP、反向代理服务器,同时也是一个通用代理服务器 (TCP/UDP/IMAP/POP3/SMTP)

它具有高性能,占用内存少,并发能力强的优点,最大可以支持 50000 个并发连接数

# Nginx 的常用命令

安装

当然,在开始使用之前,你需要先下载 Nginx

官网链接:http://nginx.org/en/download.html

下载完毕后,解压即可使用,目录是这样的

image-20230422105838529

目录树如下

├── client_body_temp
├── conf                             # Nginx所有配置文件的目录
│   ├── fastcgi.conf                 # fastcgi相关参数的配置文件
│   ├── fastcgi.conf.default         # fastcgi.conf的原始备份文件
│   ├── fastcgi_params               # fastcgi的参数文件
│   ├── fastcgi_params.default       
│   ├── koi-utf
│   ├── koi-win
│   ├── mime.types                   # 媒体类型
│   ├── mime.types.default
│   ├── nginx.conf                   # Nginx主配置文件
│   ├── nginx.conf.default
│   ├── scgi_params                  # scgi相关参数文件
│   ├── scgi_params.default  
│   ├── uwsgi_params                 # uwsgi相关参数文件
│   ├── uwsgi_params.default
│   └── win-utf
├── fastcgi_temp                     # fastcgi临时数据目录
├── html                             # Nginx默认站点目录
│   ├── 50x.html                     # 错误页面优雅替代显示文件,例如当出现502错误时会调用此页面
│   └── index.html                   # 默认的首页文件
├── logs                             # Nginx日志目录
│   ├── access.log                   # 访问日志文件
│   ├── error.log                    # 错误日志文件
│   └── nginx.pid                    # pid文件,Nginx进程启动后,会把所有进程的ID号写到此文件
├── proxy_temp                       # 临时目录
├── sbin                             # Nginx命令目录
│   └── nginx                        # Nginx的启动命令
├── scgi_temp                        # 临时目录
└── uwsgi_temp                       # 临时目录

常用命令

PS:默认情况下,你需要在目录位置对应的控制台下才能运行这些命令

# 启动 (启动效果可以访问 http://localhost:80 查看)
$ nginx # 会一直阻塞 cmd 窗口
$ start nginx # 打印日志后可以退出
# 查看版本 (-v 换成 - V 可以查看更详细的版本信息)
$ nginx -v
nginx version: nginx/1.24.0
# 检查配置语法是否正确
$ nginx -t
nginx: the configuration file G:\program\nginx-1.24.0/conf/nginx.conf syntax is ok
nginx: configuration file G:\program\nginx-1.24.0/conf/nginx.conf test is successful
# 重新载入 Nginx
# 当配置信息修改时,使用这个命令重新加载 nginx 配置
$ nginx -s reload
# 停止 nginx
# PS:-s 是代表向 nginx 进程发送通知的意思
$ nginx -s stop # 强制停止 nginx
$ nginx -s quit # 有序停止 nginx
# 查看全部命令
$ nginx -h
nginx version: nginx/1.24.0
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: NONE)
  -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

# Nginx 配置

这里循序渐进地对 Nginx 配置做一些简单的介绍

# 从默认的 Nginx 文件开始

nginx 的默认配置文件 nginx.conf 在安装目录 conf 文件夹下

在移除所有 Nginx 注释后,我们可以得到以下配置

# 全局块
worker_processes  1;	
# event 块
events {	
    worker_connections  1024;
}
# http 块
http {	
    include       mime.types;
    default_type  application/octet-stream;
    sendfile        on;
    keepalive_timeout  65;
    # server 块
    server {
        listen       80;
        server_name  localhost;
        
        # location 块
        location / {	
            root   html;
            index  index.html index.htm;
        }
        
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
}

其中,每个块的作用如下,这里我们重点关注 serverlocation 块即可

  • 全局块:nginx 全局块是指 nginx 配置文件中的最外层块,它可以包含多个指令和其他块,用于定义全局的配置和行为,
  • events 块:主要用于配置事件驱动模型和连接数限制。
  • http 块:用于定义 HTTP 全局配置。处理代理,缓存,日志定义等绝大多数功能和第三方模块的配置,比如文件引入,mime-type 定义,日志自定义,是否使用 sendfile 传输文件,连接超时时间,单连接请求数等。
  • server 块:配置虚拟主机的相关参数。
  • location 块:定义一个 URI 或者 URI 模式对应的请求处理规则。

# location 块的处理规则

Location 的具体语法为

location [ = | ~ | ~* | ^~ ] uri { ... }

例子如下

PS:不知道为什么,匹配大小写的设置无法生效,可能是 window 的 nginx 有问题?或者是 nginx 新版本有什么问题... 以后再看看吧

server {
    listen 9998;    # 监听 9998 端口
    server_name localhost;  # 监听的服务器名字
    # = 表示精确匹配
    location = /getUser {
        add_header Content-Type "application/json";
        return 200 "{id: 1, age: 16}";
    }
    # ~ 表示区分大小写的正则匹配
    location ~ ^/getData$ {
        add_header Content-Type "application/json";
        return 200 "{id: 2, age: 16}";
    }
    # ~* 表示不区分大小写的正则匹配
    location ~* ^/list$ {
        add_header Content-Type "application/json";
        return 200 "[1,2,3]";
    }
    # ^~ 表示 url 以某个字符串开头
    # 当访问 http://localhost:9998/img/1.jpg 时,可以访问到静态文件服务器下的图片
    location ^~ /img/ {    # 指定匹配的路径
        root static/; # 指定 web 服务器的根目录
        index index.html; # 指定默认的主页文件名
    }
}
server {
    listen 9997;
    server_name localhost;
    # 表示通用匹配
    # 当访问 http://localhost:9998/css/index.css 时,可以访问到静态文件服务器下的样式文件
    location /css {
        root static/;
        index index.html;
    }
    # 表示通用匹配
    location / {
        add_header Content-Type "text/plain";
        return 200 "Hello World";
    }
}

相对简单,用到时再查就完事

这里值的顺便一提的是,如果有多个 location 规则可以命中,那么会遵循一定的优先级配置来进行返回。

  • 检查使用前缀字符串(也就是通用匹配开头)的 locations,在使用前缀字符串的 locations 中选择最长匹配的,并将结果进行储存
  • 如果匹配到符合带有 = 修饰符的 URI,则立刻停止匹配
  • 如果符合带有 ^~ 修饰符的 URI,则也立刻停止匹配。
  • 然后按照定义文件的顺序,检查正则表达式(也就是前面带 ~*~ 修饰符的),匹配到就停止
  • 当正则表达式匹配不到的时候,使用之前储存的前缀字符串

# 一些简单的 Nginx 模板

# 加载自定义配置

这里我们用 include 指令加载 nginx 的相关配置

http {
    include  mime.types;
    include  config/*.conf;	# 加载 nginx/conf/config 目录下的所有配置文件
}

# 启动一个静态网站服务

nginx/conf/config 目录下新建一个 static-server.conf 文件,然后加上以下配置

# 开启一个本地代理服务器
server {
    listen 9999;    # 监听 9999 端口
    server_name localhost;  # 监听的服务器名字
    # 开启 gzip 压缩
    gzip on;
    gzip_min_length 1k; # 最小压缩单位
    gzip_types application/javascript;  # 取自响应头 Content-Type
    gzip_static on; # 启用预先压缩的 Gzip 文件(也就是搜索同目录下的.gz 文件返回)
    location / {    # 指定匹配的路径
        root static/; # 指定 web 服务器的根目录
        index index.html; # 指定默认的主页文件名
    }
}

然后,在 /static 目录下,加上一些文件

image-20230425174325432

调用 nginx -s reload 重启配置后,就可以通过访问 localhost:9999 查看效果了

image-20230426175239196

# 单页应用路由处理

总所周知,如果你的路由使用了 history 模式,那么在访问 localhost:8080/about 这种链接时,会返回 404

原因是 nginx 会把这个连接当成一个文件去处理(你可以在 dist 目录下加一个 about 文件来验证),而 nginx 找不到这个文件,所以就会返回 404

# 开启一个本地代理服务器
server {
    listen 9996;    # 监听 9996 端口
    server_name localhost;  # 监听的服务器名字
    location / {    # 指定匹配的路径
        root dist/; # 指定 web 服务器的根目录
        index index.html; # 指定默认的主页文件名
        try_files $uri $uri/ /index.html;   # 将所有路由请求重定向到 Vue 应用的入口文件 (index.html)
    }
}

我们仅需在静态网站的基础上,添加一行 try_files $uri $uri/ /index.html 配置即可,这样我们就可以将路由请求重定向回 index.html 了(但注意,不要在目录下存放和路由同名的文件,不然的话,就会访问那个同名的文件)。

image-20230505161624645

# 开启防盗链

只要在 nginx 中加入以下的配置即可

# 开启一个本地代理服务器
server {
    listen 9999;    # 监听 9999 端口
    server_name localhost;  # 监听的服务器名字
    # ... 其他配置省略
    
    # 防盗链配置
    location ~* .*\.(gif|jpg|png)$ {
        root static/;
        valid_referers localhost;   # 仅限 localhost 访问
        if ($invalid_referer) {
            # 配置返回 403
            # return 403;
            # 配置:重定向请求
            rewrite ^/ https://s2.loli.net/2023/04/25/HyDgp25ZsVxwJ83.png;
            break;
        }
    }
}

# 负载均衡

平均将请求转发到两台服务器上

upstream webapp {
    server localhost:9996 weight=1;
    server localhost:9999 weight=1;
    random two least_conn;
}
server {
    listen 9995;
    location / {
        proxy_pass http://webapp;
    }
}

# 跨域

跨域的配置非常简单

server {
    listen 8070;
    location / {
        # 允许跨域的请求,可以自定义变量 $http_origin,* 表示所有,always 表示所有情况下都返回
        add_header 'Access-Control-Allow-Origin' * always;
        # 允许携带 cookie 请求
        add_header 'Access-Control-Allow-Credentials' 'true';
        # 允许跨域请求的方法:GET,POST,OPTIONS,PUT
        add_header 'Access-Control-Allow-Methods' 'GET,POST,OPTIONS,PUT';
        # 允许请求时携带的头部信息,* 表示所有
        add_header 'Access-Control-Allow-Headers' *;
        # 允许发送按段获取资源的请求
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
        # 一定要有!!!否则 Post 请求无法进行跨域!
        # 在发送 Post 跨域请求前,会以 Options 方式发送预检请求,服务器接受时才会正式请求
        if ($request_method = 'OPTIONS') {
            add_header 'Access-Control-Max-Age' 1728000;
            add_header 'Content-Type' 'text/plain; charset=utf-8';
            add_header 'Content-Length' 0;
            # 对于 Options 方式的请求返回 204,表示接受跨域请求
            return 204;
        }
        # 对 localhost:8070 所有的请求都会被转发到 https://server.com
        proxy_pass https://server.com;
    }
}

请求的时候使用以下代码

let baseUrl = IsDev ? "http://localhost:8070/" : `https://server.com`;	// 根据环境选择前缀
let instance = axios.createInstance({
    baseUrl: baseUrl
});
(async function () {
    let data = await instance.get("/data");
    console.log('data', data);
})()

# 参考

前端仔也需要懂的 nginx 内容:https://juejin.cn/post/7007346707767754765

Nginx location 匹配规则:https://www.cnblogs.com/woshimrf/p/nginx-config-location.html

Nginx 配置文件介绍:https://zhuanlan.zhihu.com/p/396032376

nginx.conf 配置文件详解:https://juejin.cn/post/6844903741678698510

nginx 反向代理解决跨域问题:https://juejin.cn/post/6995374680114741279

nginx 一网打尽:https://juejin.cn/post/7112826654291918855#heading-13

更新于 阅读次数