在开发完成后,部署代码到服务器已经是每一个开发者必须掌握的技能。将代码部署到服务器,前端web最常用就是部署到nginx服务器中。nginx具有反向代理、负载均衡、热更新等优点。 每次部署在网上找点资料也能够解决,但是没有完全掌握nginx的每个配置属性时,遇到问题就很难排查。工作中就影响效率,因此很有必要对nginx有一个全面的了解。从安装到配置,到日志查看,错误排查等一一逐步分析。让你从ng小白蜕变位nginx大牛。
安装
在服务有2种安装nginx的方式
- 直接从官网下载安装包,解压到服务器对应的目录
- 使用服务器的 yum 命令进行黑盒命令安装。
从官网下载包到服务器
arduino
wget http://nginx.org/download/nginx-1.7.9.tar.gz
解压nginx包
tar zxvf nginx-1.6.2.tar.gz
进入解压后的安装包目录,之后编译安装
bash
cd nginx-1.7.9
// 指定nginx安装目录为/usr/local/nginx
./configure --prefix=/usr/local/nginx
// 之后进行编译
make&make install
注意:安装前需要先安装c语言的环境
bash
yum -y install gcc-c++
yum -y install pcre pcre-devel
yum -y install zlib zlib-devel
yum -y install openssl openssl-devel
使用yum命令进行安装
javascript
yum -y install nginx
安装完成后,通过 rpm -ql nginx 命令查看 Nginx 的安装信息:
bash
# Nginx配置文件
/etc/nginx/nginx.conf # nginx 主配置文件
/etc/nginx/nginx.conf.default
# 可执行程序文件
/usr/bin/nginx-upgrade
/usr/sbin/nginx
# nginx库文件
/usr/lib/systemd/system/nginx.service # 用于配置系统守护进程
/usr/lib64/nginx/modules # Nginx模块目录
# 帮助文档
/usr/share/doc/nginx-1.16.1
/usr/share/doc/nginx-1.16.1/CHANGES
/usr/share/doc/nginx-1.16.1/README
/usr/share/doc/nginx-1.16.1/README.dynamic
/usr/share/doc/nginx-1.16.1/UPGRADE-NOTES-1.6-to-1.10
# 静态资源目录
/usr/share/nginx/html/404.html
/usr/share/nginx/html/50x.html
/usr/share/nginx/html/index.html
# 存放Nginx日志文件
/var/log/nginx
- yum 安装:在线安装,好处是:安装方式简单,不易出错;yum安装会安装到全局,可以直接使用nginx命令进行重启、关闭等操作哦
- 源码包 安装:先将 nginx 的源码下载下来,在自己的系统里编译生成可执行文件,然后执行,好处是:因为是在自己的系统上编译的,更符合自己系统的性能,可以进行个性化配置。安装包路径可以自己设置,命令不是全局的,每次重启需要进入到对应的目录下。
正向代理和反向代理
经常说nginx是反向代理,那么什么是反向代理,是否还存在正向代理,他们有什么区别?
- 反向代理是为服务端服务的
- 正向代理是为客户端使用的
操作nginx常用命令
bash
# 启动nginx
$ nginx
# 重启nginx
$ nginx -s reload
# 查看nginx的配置
$ nginx -T
# 检查nginx配置语法是否正确
$ nginx -t
# 关闭nginx
$ nginx -s stop
nginx.conf配置文件结构
nginx
# main段配置信息
user nginx; # 运行用户,默认即是nginx,可以不进行设置
worker_processes auto; # Nginx 进程数,一般设置为和 CPU 核数一样
error_log /var/log/nginx/error.log warn; # Nginx 的错误日志存放目录
pid /var/run/nginx.pid; # Nginx 服务启动时的 pid 存放位置
# events段配置信息
events {
# 使用epoll的I/O模型(不知道Nginx该使用哪种轮询方法,会自动选择一个最适合你操作系统的)
use epoll;
worker_connections 1024; # 每个进程允许最大并发数
}
# http段配置信息
# 配置使用最频繁的部分,代理、缓存、日志定义等绝大多数功能和第三方模块的配置都在这里设置
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段配置信息
location / {
root /usr/share/nginx/html; # 网站根目录
index index.html index.htm; # 默认首页文件
deny 1.29.39.22; # 禁止访问的ip地址,可以为all
allow 1.116.33.29;# 允许访问的ip地址,可以为all
}
error_page 500 502 503 504 /50x.html; # 默认50x对应的访问页面
error_page 400 404 error.html; # 同上
}
}
- main 全局配置,对全局生效;
- events 配置影响 Nginx 服务器与用户的网络连接;
- http 配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置;
- server 配置虚拟主机的相关参数,一个 http 块中可以有多个 server 块;
- location 用于配置匹配的 uri ;
- upstream 配置后端服务器具体地址,负载均衡配置不可或缺的部分;
配置层级结构
匹配方式
主要是http模块下的server模块,进行不同uri的匹配;
location匹配规则
location 的匹配优先级规则:
- = 表示精确匹配。只有请求的url路径与后面的字符串完全相等时,才会命中。
- ^~xx 表示如果以xx开头,不区分大小写。
- ~ 表示该规则是使用正则定义的,区分大小写。
- ~*xx 表示以xx结尾,不区分大小写。如图片格式结尾的资源
nginx 的匹配优先顺序按照上面的顺序进行优先匹配,只要某一个匹配命中直接退出,不再进行往下的匹配。 精确匹配(=) > 高优先级前缀匹配(^~) > 正则匹配(~ ~*) > 普通前缀匹配
对规则进行测试验证:
nginx
location = /111/ {
default_type text/plain;
return 200 "111 success";
}
location /222 {
default_type text/plain;
return 200 $uri;
}
location ~ ^/333/bbb.*\.html$ {
default_type text/plain;
return 200 $uri;
}
location ~* ^/444/AAA.*\.html$ {
default_type text/plain;
return 200 $uri;
}
绝对匹配
- 第一条路由
location = /111/
路径必须完全匹配
location 和路径之间加了个 =,代表精准匹配,也就是只有完全相同的 url 才会匹配这个路由。
相对匹配
- 第二条路由
location /222
不带 = 代表根据前缀匹配,后面可以是任意路径;$uri 是表示取当前路径
正则匹配
- 第三条路由
location ~ ^/333/bbb.*\.html$
想支持正则,就可以加个 ~, 该正则表示以 /333/bbb 开头,然后中间是任意字符,最后 .html 结尾的 url
正则不区分大小写的匹配
但是该匹配是区分大小写的
- 让正则不区分大小写,可以再加个 *;第四条路由
location ~* ^/444/AAA.*\.html$
,表示以 444/AaA开头,和html结尾。此时Aaa不用区分大小写。
提高匹配优先级
- 可以提高优先级,可以使用 ^~
nginx
location ^~ /444 {
default_type text/plain;
return 200 'xxxx';
}
总结一下,一共 4 个 location 语法: location = /aaa 是精确匹配 /aaa 的路由。 location /bbb 是前缀匹配 /bbb 的路由。 location ~ /ccc.*.html 是正则匹配。可以再加个 * 表示不区分大小写 location * /ccc.*.html location ^ /ddd 是前缀匹配,但是优先级更高 图片来源
nginx 每条规则都要以分号结尾,可以运行 nginx -t nginx.conf 查看配置规则是否有误
root和alias的区别
nginx
server {
listen 80;
server_name localhost;
# 用root方式,location中的路径会拼加到root的地址后面
# 用户输入请求路径为:http://localhost:8080/files/index.jpg
# 服务器内查询地址为:/dist/assets/files/index.jpg
location ~^/files/ {
root /dist/assets/;
index index.html index.htm;
}
# 用alias方式,location中的路径不会拼加到alias的地址后面
# 用户输入请求路径为:http://localhost:8080/files/index.jpg
# 服务器内查询地址为:/dist/assets/index.jpg
location ~^/files/ {
alias /dist/assets/;
index index.html index.htm;
}
}
如果是查询匹配静态资源,推荐使用alias进行指定路径。
location和proxy_pass路径结尾是否有/的区别
nginx
server {
listen 8080;
server_name localhost;
# ----- 01 代理地址不加斜杠 【会拼上location的地址】
# 用户请求路径为:http://127.0.0.1:8080/api/getInfo
# 服务器实际代理为:http://127.0.0.1:8000/api/getInfo
location ^~/api/ {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $http_host; #后台可以获取到完整的ip+端口号
proxy_set_header X-Real-IP $remote_addr; #后台可以获取到用户访问的真实ip地址
}
# 请求路径为:http://127.0.0.1:8080/api/getInfo
# 服务器实际代理为:http://127.0.0.1:8000/api/getInfo
location ^~/api {
proxy_pass http://127.0.0.1:8000;
proxy_set_header Host $http_host; #后台可以获取到完整的ip+端口号
proxy_set_header X-Real-IP $remote_addr; #后台可以获取到用户访问的真实ip地址
}
# ----- 02 代理地址+斜杠 【不会拼接上location的路径】
# 请求路径为:http://127.0.0.1:8080/api/getInfo
# 服务器实际代理为:http://127.0.0.1:8000/getInfo
location ^~/api/ {
proxy_pass http://127.0.0.1:8000/;
proxy_set_header Host $http_host; #后台可以获取到完整的ip+端口号
proxy_set_header X-Real-IP $remote_addr; #后台可以获取到用户访问的真实ip地址
}
# 请求路径为:http://127.0.0.1:8080/api/getInfo
# 服务器实际代理为:http://127.0.0.1:8000//getInfo
location ^~/api {
proxy_pass http://127.0.0.1:8000/;
proxy_set_header Host $http_host; #后台可以获取到完整的ip+端口号
proxy_set_header X-Real-IP $remote_addr; #后台可以获取到用户访问的真实ip地址
}
# ----- 03 代理地址+后缀 【不会拼location的地址】
# 请求路径为:http://127.0.0.1:8080/api/getInfo
# 服务器实际代理为:http://127.0.0.1:8000/user/getInfo
location ^~/api {
proxy_pass http://127.0.0.1:8000/user;
proxy_set_header Host $http_host; #后台可以获取到完整的ip+端口号
proxy_set_header X-Real-IP $remote_addr; #后台可以获取到用户访问的真实ip地址
}
# 请求路径为:http://127.0.0.1:8080/api/getInfo
# 服务器实际代理为:http://127.0.0.1:8000/usergetInfo
location ^~/api/ {
proxy_pass http://127.0.0.1:8000/user;
proxy_set_header Host $http_host; #后台可以获取到完整的ip+端口号
proxy_set_header X-Real-IP $remote_addr; #后台可以获取到用户访问的真实ip地址
}
# ----- 04 代理地址+后缀+斜杠
# 请求路径为:http://127.0.0.1:8080/api/getInfo
# 服务器实际代理为:http://127.0.0.1:8000/user/getInfo
location ^~/api/ {
proxy_pass http://127.0.0.1:8000/user/;
proxy_set_header Host $http_host; #后台可以获取到完整的ip+端口号
proxy_set_header X-Real-IP $remote_addr; #后台可以获取到用户访问的真实ip地址
}
# 请求路径为:http://127.0.0.1:8080/api/getInfo
# 服务器实际代理为:http://127.0.0.1:8000/user//getInfo
location ^~/api
proxy_pass http://127.0.0.1:8000/user/;
proxy_set_header Host $http_host; #后台可以获取到完整的ip+端口号
proxy_set_header X-Real-IP $remote_addr; #后台可以获取到用户访问的真实ip地址
}
}
可以结合路径请求,然后查看access.log日志,查看发送到服务器的路径;
- proxy_pass 如果有uri参数时,那么就和location后缀保持一致,都加/ 或者都不加/
- proxy_pass 没有uri时
- 有/时,不会拼接 location 的地址
- 无/时,会拼接 location 的地址
bash
tail -f /var/log/nginx/access.log
利用nginx解决开发中的常见问题
静态资源解析
Nginx可以作为静态资源服务器,获取服务器的目录结构。可以提供一个上传功能,这样其他的应用如果需要静态资源就可以从该静态资源服务器中获取。
nginx
server {
listen 8999;
server_name localhost;
root /var/www/dist/;
location ^~/tree/ {
# 设置显示的目录
alias /opt/homebrew/var/log/;
# 开启目录结构
autoindex on;
# 添加缓存
add_header Cache-Control "public";
# 默认为on,显示出文件的大小,单位是bytes。一般会改为off
autoindex_exact_size off;
# on显示文件的本地时间;否则显示文件的GMT时间。默认是off
autoindex_localtime on;
# 解决文件名中文乱码
charset utf-8;
}
}
设置SSL/TLS加密
给访问的网站设置为https规则。https实质上是设置的访问加密,通过ng可以对访问资源进行加密; http默认端口是80,https的默认端口是443;添加下面2个加密属性配置。
- ssl_certificate:用于指定SSL证书文件的路径;SSL证书文件包含用于验证服务器身份的公钥以及相关信息,如服务器的域名和证书颁发机构(CA)的签名。
- ssl_certificate_key:指定SSL证书的私钥文件的路径;私钥文件通常包含与SSL证书配对的私钥,用于加密和解密通信中的数据。私钥是SSL/TLS加密的关键组成部分,它用于生成加密的会话密钥和对数据进行解密。
nginx
http {
server {
listen 443 ssl;
ssl_certificate /etc/nginx/cert.pem;
ssl_certificate_key /etc/nginx/cert.key;
location / {
proxy_pass http://backend;
}
}
}
开发时生成临时证书,生成ssl证书 只是测试开发时使用,正式网站需要到正规的认证机构申请,阿里云或腾讯云等都可以;
前端访问接口是CORS跨域问题
前端项目访问接口时经常会出现报错 No 'Access-Control-Allow-Origin' header is present on the requested resource报错等,如果后端服务没有设置header参数,那么就可以给Nginx服务器配置相应的header参数:
nginx
location / {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods 'GET, POST, OPTIONS';
add_header Access-Cpntrol-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Alive,X-Requseted-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
}
开启静态资源的gzip压缩
有一些图片、视频、音频资源放到nginx服务器,可以开启gzip资源压缩。 也可以对比较大的js或css文件压缩。 gzip是网页压缩的一种方式,经过gzip压缩后,网页大小可以变为原来的30%甚至更小,可以节约大量的带宽,提高传输效率,响应速度更快,提升用户体验。
nginx
server{
# 开启或 关闭gzip模块
gzip on;
# 设置系统获取几个单位的缓存用于存储gzip的压缩结果数据流。
gzip_buffers 32 4K;
# 压缩级别,1-10,数字越大压缩的越好,压缩级别越高压缩率越大,压缩时间越长。
gzip_comp_level 6;
# 设置允许压缩的页面最小字节数,页面字节数从相应消息头的Content-length中进行获取。
gzip_min_length 100;
gzip_types application/javascript text/css text/xml;
# IE6对Gzip不友好,对Gzip(可以通过该指令对一些特定的User-Agent不使用压缩功能)
gzip_disable "MSIE [1-6]\.";
# 用于设置启用或禁用从代理服务器上收到相应内容gzip压缩。
gzip_proxied on;
# 识别HTTP协议版本,其值可以是 1.1 或 1.0
gzip_http_version 1.1;
# 用于设置启用或禁用从代理服务器上收到相应内容gzip压缩。
gzip_proxied : off;
# 用于在响应消息头中添加Vary:Accept-Encoding,
# 使代理服务器根据请求头中的Accept-Encoding识别是否启用gzip压缩。
gzip_vary on;
}
upstream 设置负载均衡
面对着C端大量用户的服务,很可能同时存在每秒钟上亿 个请求,一台服务器处理不过来,分分钟会挂掉。这样就可以使用nginx的扩展服务器,设置负载均衡,让请求分发到不同的服务器。 实现高可用、高并发以及服务器性能优化。 nginx 负载均衡 主要是通过配置 upstream 来实现的: upstream参数配置
down | 当前sever暂时不参与负载均衡 |
---|---|
backup | 预留备份的服务器 |
max_fails | 允许请求失败的次数 |
fail_timeout | 经过max_fails失败后,服务器暂停的时间 |
max_conns | 限制最大的连接数,防止一些低性能服务器收到过多请求 |
调度算法,upstream设置
正常轮询 | 按照时间顺序将请求均分到不同服务器 |
---|---|
加权轮询weight | weight值越大,被访问到的几率越大 |
ip_hash | 每个请求按访问IP的hash结果分配,来自同一个IP固定访问一个服务器 |
least_conn | 最少链接数,哪个机器连接数少就分发 |
url_hash | 按照访问的URL的hash结果分配,每个URL定向到同一个后端服务器 |
hash关键值 | hash自定义的key |
首先使用node建立3个服务器端口9000,9001,9002 打开浏览器访问 http://test
javascript
const http = require("http");
const server = http.createServer();
const host = "0.0.0.0";
const port = 9000;
let n = 0;
server.on("request", function(req,res){
n += 1;
console.log("request coming!",n);
res.write("hello")
res.end();
});
server.listen(port, host, function(req,res){
console.log("server is running: http://",host,":",port);
})
另外2个类似,只用改下端口
轮询策略
修改nginx.conf
javascript
upstream test {
server 0.0.0.0:9000;
server 0.0.0.0:9001;
server 0.0.0.0:9002;
}
server {
listen 5678;
server_name localhost;
location / {
proxy_pass http://test;
}
}
改动是比较简单的,增加了一个 upsteam 配置。这个是最简单的 轮询策略,大量请求打过来后,nginx 将这些请求 平均 分配到 3 台服务器上。会把请求均匀的分发到3个服务器上
加权轮询策略
服务地址后面加上 weight 参数来表示权重, 这里的意思是 9000 端口处理50%的请求, 9001 端口处理25%的请求, 9002 端口处理25%的请求。 修改nginx.conf
javascript
upstream test {
server 0.0.0.0:9000 weight=2;
server 0.0.0.0:9001 weight=1;
server 0.0.0.0:9002 weight=1;
}
server {
listen 5678;
server_name localhost;
location / {
proxy_pass http://test;
}
}
ip_hash 策略
根据 ip 来分配请求。固定的客户端发出的请求会被固定分配到一台服务器。接着修改 nginx.conf 配置 修改nginx.conf
javascript
upstream test {
ip_hash;
server 0.0.0.0:9000;
server 0.0.0.0:9001;
server 0.0.0.0:9002;
}
server {
listen 5678;
server_name localhost;
location / {
proxy_pass http://test;
}
}
既然是根据 ip 来分配请求的,那我本机发 100 个请求,这 100 个请求应该会被打到同一台服务器上,另外两台接收到的请求数量为 0
日志查看
accessLog查看
在http模块以及下边的server模块,会存在相同的变量参数设置,如果server内没有设置,则会使用外部共用的。如果server内存在,使用server的。 同理server的变量和server下的location变量,也是遵循相同的替代规则。
使用 GoAccess 分析 Nginx 日志
GoAccess 是一款开源的且具有交互视图界面的实时 Web 日志分析工具,通过你的 Web 浏览器或者 *nix 系统下的终端程序 (Terminal) 即可访问。GoAccess 官网:goaccess.io
安装GoAccess
使用yum命令安装
javascript
yum install goaccess
或者下载安装包,解压
javascript
$ wget -O - https://deb.goaccess.io/gnugpg.key | gpg --dearmor | sudo tee /usr/share/keyrings/goaccess.gpg >/dev/null
$ echo "deb [signed-by=/usr/share/keyrings/goaccess.gpg arch=$(dpkg --print-architecture)] https://deb.goaccess.io/ $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/goaccess.list
$ sudo apt-get update
$ sudo apt-get install goaccess
配置GoAccess
GoAccess 的主要配置文件为 goaccess.conf,主要配置参数有以下几项:
javascript
time-format %H:%M:%S
date-format %d/%b/%Y
log-format %h %^[%d:%t %^] "%r" %s %b "%R" "%u"
log-format 与 Nginx 日志 access.log 中的 log_format 格式对应,每个参数以空格或者制表符分割。参数说明如下:
javascript
%t 匹配 time-format 格式的时间字段
%d 匹配 date-format 格式的日期字段
%h host(客户端 ip 地址,包括 ipv4 和 ipv6)
%r 来自客户端的请求行
%m 请求的方法
%U URL 路径
%H 请求协议
%s 服务器响应的状态码
%b 服务器返回的内容大小
%R HTTP 请求头的 referer字段
%u 用户代理的 HTTP 请求报头
%D 请求所花费的时间,单位微秒
%T 请求所花费的时间,单位秒
%^ 忽略这一字段
使用 GoAccess
在终端模式下运行 GoAccess
javascript
$ goaccess -a -d -f /var/log/nginx/hi-linux.com.access.log -p /etc/goaccess.conf
在 HTML 模式下运行 GoAccess
默认情况下,GoAccess 是运行在控制台模式下的,命令行里直接输出结果。 还可以通过 GoAccess 的 HTML 模式将分析结果页面保存为 HTML,更方便的通过浏览器访问。要让 GoAccess 在 HTML 模式下运行,我们则需要配置下 GoAccess 的配置文件。
javascript
$ cat goaccess.conf
time-format %H:%M:%S
date-format %d/%b/%Y
log-format %h %^[%d:%t %^] "%r" %s %b "%R" "%u"
通过命令行运行 GoAccess 的 HTML 模式
javascript
$ goaccess -a -d -f /var/log/nginx/go.access.log -p /etc/goaccess.conf -o /var/log/nginx/goaccess.html
报错问题
nginx error open nginx.pid
解决: /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf 原理:使用nginx -c的参数指定nginx.conf文件的位置;很大原因是nginx开机未启动。
安装完成Nginx后无法站外访问
使用curl可以在本服务器访问,但是在外部无法访问。多数是由于服务器设置的防火墙 解决:需要关闭防火墙,或者把nginx的端口设置为开放。