运行环境

系统:Ubuntu20.04 LTS
Nginx版本:

Nginx介绍

传统的 Web 服务器,每个客户端连接作为一个单独的进程或线程处理,需在切换任务时将 CPU 切换到新的任务并创建一个新的运行时上下文,消耗额外的内存和 CPU 时间,当并发请求增加时,服务器响应变慢,从而对性能产生负面影响。

Nginx 是开源、高性能、高可靠的 Web 和反向代理服务器,而且支持热部署,几乎可以做到 7 * 24 小时不间断运行,即使运行几个月也不需要重新启动,还能在不间断服务的情况下对软件版本进行热更新。性能是 Nginx 最重要的考量,其占用内存少、并发能力强、能支持高达 5w 个并发连接数,最重要的是,Nginx 是免费的并可以商业化,配置使用也比较简单。

Nginx 的最重要的几个使用场景:

  1. 静态资源服务,通过本地文件系统提供服务;
  2. 反向代理服务,延伸出包括缓存、负载均衡等;
  3. API 服务,OpenResty ;

对于前端来说 Node.js 不陌生了,Nginx 和 Node.js 的很多理念类似,HTTP 服务器、事件驱动、异步非阻塞等,且 Nginx 的大部分功能使用 Node.js 也可以实现,但 Nginx 和 Node.js 并不冲突,都有自己擅长的领域。Nginx 擅长于底层服务器端资源的处理(静态资源处理转发、反向代理,负载均衡等),Node.js 更擅长上层具体业务逻辑的处理,两者可以完美组合,共同助力前端开发。

作者:SHERlocked93
链接:https://juejin.cn/post/6844904144235413512
来源:掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

正向代理和反向代理

什么是代理?

代理(proxy):代理可以理解成中介,根据字意思我们也可以明白代理就是代替某人处理某事的意思。比如,我们买东西,本来可以直接去厂家购买,但是如果我们需要买很多东西就不可能一个一个厂家去买了,太过于麻烦,有头脑的人就开了超市,从厂家进货,我们直接从超市购买就可以了,当然,一般情况下,厂家也会切断直销渠道。

直接购买单人情况

直接购买复炸情况

增加代理

可以看到增加代理后逻辑清晰了许多。那么什么时候应该使用代理呢?

我把使用代理的情况分为两种:

  1. 多对多关系时,需要解决逻辑紊乱问题。
  2. 当客户端(顾客)与服务端(厂家)不能直连时,也就是厂家不能直销时。

正向代理

正向代理类似一个跳板机,代理访问外部资源

比如我们国内访问谷歌,直接访问访问不到,我们可以通过一个正向代理服务器,请求发到代理服,代理服务器能够访问谷歌,这样由代理去谷歌取到返回数据,再返回给我们,这样我们就能访问谷歌了

正向代理的用途:
(1)访问原来无法访问的资源,如google
(2) 可以做缓存,加速访问资源
(3)对客户端访问授权,上网进行认证
(4)代理可以记录用户访问记录(上网行为管理),对外隐藏用户信息

反向代理

反向代理(Reverse Proxy)实际运行方式是指以代理服务器来接受internet上的连接请求,然后将请求转发给内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外就表现为一个服务器

反向代理的作用:
(1)保证内网的安全,阻止web攻击,大型网站,通常将反向代理作为公网访问地址,Web服务器是内网
(2)负载均衡,通过反向代理服务器来优化网站的负载

正向代理和反向代理的区别

看图理解一:

看图理解二:

总结:

  1. 正向代理即是客户端代理, 代理客户端, 服务端不知道实际发起请求的客户端.
    反向代理即是服务端代理, 代理服务端, 客户端不知道实际提供服务的服务端
  2. 正向代理中,proxy和client同属一个LAN,对server透明;
    反向代理中,proxy和server同属一个LAN,对client透明。
    实际上proxy在两种代理中做的事都是代为收发请求和响应,不过从结构上来看正好左右互换了下,所以把后出现的那种代理方式叫成了反向代理
  3. 正向代理: 买票的黄牛,反向代理: 租房的代理
  4. 一个代表的是顾客, 一个是厂家代表。
  5. 正向代理隐藏的是请求的客户端,反向代理隐藏的是服务器端。

负载均衡

请求爆发式增长的情况下,单个机器性能再强劲也无法满足要求了,这个时候集群的概念产生了,单个服务器解决不了的问题,可以使用多个服务器,然后将请求分发到各个服务器上,将负载分发到不同的服务器,这就是负载均衡,核心是「分摊压力」。Nginx 实现负载均衡,一般来说指的是将请求转发给服务器集群。

举个具体的例子 🌰,晚高峰乘坐地铁的时候,入站口经常会有地铁工作人员大喇叭“请走 B 口,B 口人少车空….”,这个工作人员的作用就是负载均衡。

动静分离

为了加快网站的解析速度,可以把动态页面(动态资源)和静态页面(静态资源)由不同的服务器来解析,加快解析速度,降低原来单个服务器的压力。

Nginx安装

安装之前需要确定 gcc、make、zlib、pcre、openssl已经安装。

安装编译工具及库文件

apt install make zlib zlib-devel gcc-c++ libtool openssl openssl-devel

首先要安装pcre

百度百科介绍:https://baike.baidu.com/item/PCRE/7401536?fr=aladdin
PCRE 作用是让 Nginx 支持 Rewrite 功能。
官网:http://www.pcre.org/

箭头部分都可以找到下载地址。
我们把它弄到系统中,解压:
tar zxvf pcre-8.35.tar.gz
进入安装包目录
cd pcre-8.35
编译安装
./configure
make && make install
查看pcre版本
pcre-config --version
如果需要卸载,在pcre的解压目录下,sudo make uninstall

Nginx下载安装

nginx下载地址:https://nginx.org/en/download.html

将其下载到/usr/local/src或者/opt目录。

  1. 解压安装包
    tar -zxvf nginx-1.20.0.tar.gz
  2. 进入安装包
    cd nginx-1.20.0/
  3. 编译安装
    ./configure

    make && make install
  4. 查看nginx版本
1
2
root@VM-12-10-ubuntu:/usr/local/nginx/sbin# ./nginx -v 
nginx version: nginx/1.20.0
  1. 启动nginx
1
2
3
4
5
root@VM-12-10-ubuntu:/usr/local/nginx/sbin# ./nginx 
root@VM-12-10-ubuntu:/usr/local/nginx/sbin# ps -ef | grep nginx
root 1814766 1 0 14:58 ? 00:00:00 nginx: master process ./nginx
nobody 1814767 1814766 0 14:58 ? 00:00:00 nginx: worker process
root 1814800 1794033 0 14:58 pts/0 00:00:00 grep --color=auto nginx
  1. 浏览器直接访问 ip:80

如果无法访问,注意防火墙的配置

Nginx 操作常用命令

Nginx 的命令在控制台中输入 nginx -h 就可以看到完整的命令,这里列举几个常用的命令:

  1. 查看nginx 版本号
1
nginx -v
  1. 启动nginx
1
nginx
  1. 关闭nginx
1
nginx -s stop
  1. 重加载
1
nginx -s reload  # 向主进程发送信号,重新加载配置文件,热重启
  1. 其它
1
2
3
4
nginx -s reopen	 # 重启 Nginx
nginx -s quit # 等待工作进程处理完成后关闭
nginx -T # 查看当前 Nginx 最终的配置
nginx -t -c <配置路径> # 检查配置是否有问题,如果已经在配置目录,则不需要-c

nginx的配置

nginx的配置文件/usr/local/nginx/conf/nginx.conf

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
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#user  nobody;
worker_processes 1;

#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;

#pid logs/nginx.pid;


events {
worker_connections 1024;
}


http {
include mime.types;
default_type application/octet-stream;

#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 logs/access.log main;

sendfile on;
#tcp_nopush on;

#keepalive_timeout 0;
keepalive_timeout 65;

#gzip on;

server {
listen 80;
server_name localhost;

#charset koi8-r;

#access_log logs/host.access.log main;

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

#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#location ~ \.php$ {
# proxy_pass http://127.0.0.1;
#}

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
#}

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}


# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000;
# listen somename:8080;
# server_name somename alias another.alias;

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


# HTTPS server
#
#server {
# listen 443 ssl;
# server_name localhost;

# ssl_certificate cert.pem;
# ssl_certificate_key cert.key;

# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;

# ssl_ciphers HIGH:!aNULL:!MD5;
# ssl_prefer_server_ciphers on;

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

}

nginx.conf 结构图可以这样概括:

1
2
3
4
5
6
7
8
9
10
11
main        # 全局配置,对全局生效
├── events # 配置影响 Nginx 服务器或与用户的网络连接
├── http # 配置代理,缓存,日志定义等绝大多数功能和第三方模块的配置
│ ├── upstream # 配置后端服务器具体地址,负载均衡配置不可或缺的部分
│ ├── server # 配置虚拟主机的相关参数,一个 http 块中可以有多个 server 块
│ ├── server
│ │ ├── location # server 块可以包含多个 location 块,location 指令用于匹配 uri
│ │ ├── location
│ │ └── ...
│ └── ...
└── ...

我们把nginx配置文件分为三部分:

  1. 第一部分 全局块

    worker_processes 1;值越大,可以支持的并发处理器也越多,一般设置为CPU核心数或两倍。
  2. 第二部分 events块
    events块涉及的指令主要影响nginx服务器与用户的网络连接。
  3. 第三部分 http块
    http块是nginx配置最频繁的部分。代理、缓存和日志定义等绝大多数功能和第三方模块的配置都在这里。
    需要注意的是:http又包含http全局块server块
    ①http全局块

②server块

典型配置

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
35
36
37
38
39
40
41
42
43
44
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地址,可以为all
allow 172.168.33.44; # 允许访问的ip地址,可以为all
}

error_page 500 502 503 504 /50x.html; # 默认50x对应的访问页面
error_page 400 404 error.html; # 同上
}
}

server 块可以包含多个 location 块,location 指令用于匹配 uri,语法:

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

指令后面:

  • = 精确匹配路径,用于不含正则表达式的 uri 前,如果匹配成功,不再进行后续的查找;
  • ^~ 用于不含正则表达式的 uri; 前,表示如果该符号后面的字符是最佳匹配,采用该规则,不再进行后续的查找;
  • ~ 表示用该符号后面的正则去匹配路径,区分大小写;
  • ~* 表示用该符号后面的正则去匹配路径,不区分大小写。跟 ~ 优先级都比较低,如有多个location的正则能匹配的话,则使用正则表达式最长的那个;

如果 uri 包含正则表达式,则必须要有 ~~* 标志。

全局变量

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)

还有更多的内置预定义变量,可以直接搜索关键字「nginx内置预定义变量」可以看到一堆博客写这个,这些变量都可以在配置文件中直接使用。

nginx配置反向代理

如图所示:

Nginx负载均衡

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
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;
}
}
}

Nginx 提供了好几种分配方式,默认为轮询,就是轮流来。有以下几种分配方式:

  • 轮询,默认方式,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务挂了,能自动剔除;
  • weight,权重分配,指定轮询几率,权重越高,在被访问的概率越大,用于后端服务器性能不均的情况;
  • ip_hash,每个请求按访问 IP 的 hash 结果分配,这样每个访客固定访问一个后端服务器,可以解决动态网页 session 共享问题。负载均衡每次请求都会重新定位到服务器集群中的某一个,那么已经登录某一个服务器的用户再重新定位到另一个服务器,其登录信息将会丢失,这样显然是不妥的;
  • fair(第三方),按后端服务器的响应时间分配,响应时间短的优先分配,依赖第三方插件 nginx-upstream-fair,需要先安装;

Nginx动静分离

通过 location 指定不同的后缀名实现不同的请求转发。通过 expires 参数设置,可以使浏览器缓存过期时间,减少与服务器之前的请求和流量。具体 expires 定义:是给一个资源设定一个过期时间,也就是说无需去服务端验证,直接通过浏览器自身确认是否过期即可,所以不会产生额外的流量。此种方法非常适合不经常变动的资源。(如果经常更新的文件,不建议使用 expires 来缓存),我这里设置 3d,表示在这 3 天之内访问这个URL,发送一个请求,比对服务器该文件最后更新时间没有变化。则不会从服务器抓取,返回状态码 304,如果有修改,则直接从服务器重新下载,返回状态码 200。

1
2
3
4
5
6
7
8
9
10
11
server {
location /www/ {
root /data/;
index index.html index.htm;
}

location /image/ {
root /data/;
autoindex on;
}
}

Nginx配置高可用集群(双机热备)

如果Nginx宕机了,请求就无法使用了,保证Nginx宕机仍然能够使用,叫高可用

当主 Nginx 服务器宕机之后,切换到备份 Nginx 服务器

配置高可用的准备工作:

  1. 需要两台服务器A和B
  2. 两台服务器安装nginx
  3. 两台服务器安装keepalived
    前面两个条件已经准备好了,安装 keepalived,
1
apt install keepalived

然后编辑 /etc/keepalived/keepalived.conf 配置文件,并在配置文件中增加 vrrp_script 定义一个外围检测机制,并在 vrrp_instance 中通过定义 track_script 来追踪脚本执行过程,实现节点转移:

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
global_defs{
notification_email {
acassen@firewall.loc
}
notification_email_from Alexandre@firewall.loc
smtp_server 127.0.0.1
smtp_connect_timeout 30 // 上面都是邮件配置,没卵用
router_id LVS_DEVEL // 当前服务器名字,用hostname命令来查看
}
vrrp_script chk_maintainace { // 检测机制的脚本名称为chk_maintainace
script "[[ -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,备用机为BACKUP
interface eth0 // 网卡名字,可以从ifconfig中查找
virtual_router_id 51 // 虚拟路由的id号,一般小于255,主备机id需要一样
priority 100 // 优先级,master的优先级比backup的大
advert_int 1 // 默认心跳间隔
authentication { // 认证机制
auth_type PASS
auth_pass 1111 // 密码
}
virtual_ipaddress { // 虚拟地址vip
172.16.2.8
}
}

复制代码其中检测脚本 nginx_check.sh,这里提供一个:

1
2
3
4
5
6
7
8
9
#!/bin/bash
A=`ps -C nginx --no-header | wc -l`
if [ $A -eq 0 ];then
/usr/sbin/nginx # 尝试重新启动nginx
sleep 2 # 睡眠2秒
if [ `ps -C nginx --no-header | wc -l` -eq 0 ];then
killall keepalived # 启动失败,将keepalived服务杀死。将vip漂移到其它备份节点
fi
fi

复制代码复制一份到备份服务器,备份 Nginx 的配置要将 state 后改为 BACKUP,priority 改为比主机小。
设置完毕后各自 service keepalived start 启动,经过访问成功之后,可以把 Master 机的 keepalived 停掉,此时 Master 机就不再是主机了 service keepalived stop,看访问虚拟 IP 时是否能够自动切换到备机 ip addr。

再次启动 Master 的 keepalived,此时 vip 又变到了主机上。

Nginx基本原理



一个master和多个worker有什么好处呢?

  1. 可以使用nginx -s reload 热部署
  2. 每个worker是一个独立的进程,如果一个worker出现问题,其它的worker继续进行争抢,实现请求过程,不会造成服务中断。
  3. 设置worker才是最合适的呢?worker设置和CPU核心数相同最为适宜。
  4. 连接数worker_connection
    第一个:发送请求,占用了worker的几个连接数?
    答案:2或者4个

第二个:nginx有一个master,有四个worker,每个woker支持最大的连接数1024,支持的最大并发数是多少?
普通的静态访问最大并发数是:worker_connections*worker_processes/2
而如果HTTP作为反向代理来说,最大并发数量应该是worker_connections*worker_processes/4。

参考

  1. https://juejin.cn/post/6844904144235413512
  2. 反向代理和正向代理区别
  3. https://www.bilibili.com/video/BV1zJ411w7SV?p=17&spm_id_from=pageDriver