# Nginx

Java-nginx 分布式框架_免费高速下载 | 百度网盘 - 分享无限制 (baidu.com)

# nginx 目录结构

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
/usr/local/nginx/
├── client_body_temp
├── conf # 配置文件目录
│   ├── fastcgi.conf # fastcgi 配置文件
│   ├── fastcgi.conf.default
│   ├── fastcgi_params
│   ├── fastcgi_params.default
│   ├── koi-utf
│   ├── koi-win
│   ├── mime.types
│   ├── mime.types.default
│   ├── nginx.conf
│   ├── nginx.conf.default
│   ├── scgi_params
│   ├── scgi_params.default
│   ├── uwsgi_params
│   ├── uwsgi_params.default
│   └── win-utf
├── fastcgi_temp
├── html
│   ├── 50x.html
│   └── index.html
├── logs
│   ├── access.log
│   ├── error.log
│   └── nginx.pid # nginx的process id
├── proxy_temp
│   ├── 1
│   │   └── 00
│   ├── 2
│   │   └── 00
│   ├── 3
│   │   └── 00
│   ├── 4
│   │   └── 00
│   ├── 5
│   │   └── 00
│   └── 6
│   └── 00
├── sbin
│   └── nginx
├── scgi_temp
├── ssl
│   ├── 9584217_www.radsm.co.key
│   ├── 9584217_www.radsm.co_nginx.zip
│   ├── 9584217_www.radsm.co.pem
│   ├── nginx.crt
│   └── nginx.key
└── uwsgi_temp


CGl (Common Gateway Interface) 通用网关【接口】,主要解决的问题是从客户端发送一个请求和数据,服务端获取到请求和数据后可以调用调用 CGI【程序】处理及相应结果给客户端的一种标准规范。

fastcgi,scgi,uwsgi 是 cgi 的升级版

koi-utf、koi-win、win-utf 这三个文件都是与编码转换映射相关的配置文件,用来将一种编码转换成另一种编码

mime.types: 记录的是 HTTP 协议中的 Content-Type 的值和文件后缀名的对应关系

mime.types.default:mime.types 的备份文件

nginx.conf: 这个是 Nginx 的核心配置文件,这个文件非常重要,也是我们即将要学习的重点

nginx.conf.default:nginx.conf 的备份文件

logs: 记录入门的文件,当 nginx 服务器启动后,这里面会有 access.log error.log 和 nginx.pid 三个文件出现。

sbin: 是存放执行程序文件 nginx

nginx 是用来控制 Nginx 的启动和停止等相关的命令。

# nginx 启停

ps -ef | grep nginx 查看 nginx 的 master 和 work process】

Nginx 后台进程中包含一个 master 进程和多个 worker 进程,master 进程主要用来管理 worker 进程,包含接收外界的信息,并将接收到的信号发送给各个 worker 进程,监控 worker 进程的状态,当 worker 进程出现异常退出后,会自动重新启动新的 worker 进程。而 worker 进程则是专门用来处理用户请求的,各个 worker 进程之间是平等的并且相互独立,处理请求的机会也是一样的。nginx 的进程模型,我们可以通过下图来说明下:

1
2
3
4
5
6
7
[root@hecs-346515 ~]# ps -ef | grep nginx 
root 19255 19235 0 10:01 pts/0 00:00:00 grep --color=auto nginx
root 32091 1 0 Apr14 ? 00:00:00 nginx: master process /usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf
nobody 32092 32091 0 Apr14 ? 00:00:00 nginx: worker process

[root@hecs-346515 ~]# cat /usr/local/nginx/logs/nginx.pid
32091

nginx 是多进程工作模式

image-20230405205817349

信号 作用
TERM/INT 立即关闭整个服务
QUIT "优雅" 地关闭整个服务
HUP 重读配置文件并使用服务对新配置项生效
USR1 重新打开日志文件,可以用来进行日志切割
USR2 平滑升级到最新版的 nginx 会使用
WINCH 所有子进程不在接收处理新连接,相当于给 work 进程发送 QUIT 指令
1
2
3
4
5
6
kill -TERM PID
kill -QUIT PID
kill -HUP PID # master pid不会变,会重启worker,pid也会改变
kill -WINCH PID # 会关闭worker,不关闭master
kill -USR1 PID # 重新生成并打开日志
kill -USR2 PID # 生成新的master和worker,原来的pid文件变为nginx.pid.oldbin ,在QUIT关闭旧PID
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@hecs-346515 sbin]# ./nginx -?
nginx version: nginx/1.23.4
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,会输出nginxconf, mime 等其他信息
-q : suppress non-error messages during configuration testing -tq只输出错误信息
-s signal : send signal to a master process: stop, quit, reopen, reload
-p prefix : set prefix path (default: /usr/local/nginx/)
-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

1
2
3
4
5
6
7
-s singal 
stop[快速关闭,类似于TERM/INT信号的作用]
quit[优雅的关闭,类似于QUIT信号的作用]
reopen[重新打开日志文件类似于USR1信号的作用]
reload[类似于HUP信号的作用]

./nginx -g "pid logs/abc.pid" # 指定启动时的全局变量

# nginx 平滑升级

升级 1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 安装新版本
./configure --prefix=/usr/local/nginx
make

# 备份旧的nginx
cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old
# 复制新的nginx
cp objs/nginx /usr/local/nginx/sbin/

# 发送信号
kill -USR2 `cat /usr/local/nginx/logs/nginx.pid`

# 关闭旧的进程
kill -QUIT `cat /usr/local/nginx/logs/nginx.pid.oldbin`

升级 2:

1
2
3
4
5
6
7
# 备份旧的nginx
cp /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old

# 安装新版本
./configure --prefix=/usr/local/nginx
cp objs/nginx /usr/local/nginx/sbin/
make upgrade
1
加减模块可升级基本一样,只是不发送最后的信号

# 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
# vim /usr/local/nginx/conf/nginx.conf
worker_processes 1;

# 全局块

events {
# events块 配置网络连接相关内容
worker_connections 1024;
}

# 全局块

http {
# http块
include mime.types;
default_type application/octet-stream;
sendfile on;
keepalive_timeout 65;

server {
# server块,配置虚拟主机等功能,一个http可以由多个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;
}
}

}

nginx.conf 配置文件中默认有三大块:全局块、events 块、http 块

http 块中可以配置多个 server 块,每个 server 块又可以配置多个 location 块。

# 全局块

1
2
3
4
5
6
7
8
| 语法   | user user [group] |
| ------ | ----------------- |
| 默认值 | nobody |
| 位置 | 全局块 |

user www;
# 默认为nobody
# 如果user 的值是普通用户,那么root /root/html 会产生403的错误

综上所述,使用 user 指令可以指定启动运行工作进程的用户及用户组,这样对于系统的权限访问控制的更加精细,也更加安全。

master_process: 用来指定是否开启工作进程。

语法 master_process on|off;
默认值 master_process on;
位置 全局块

用于配置 Nginx 生成工作进程的数量,这个是 Nginx 服务器实现并发处理服务的关键所在。理论上来说 workder process 的值越大,可以支持的并发处理量也越多,但事实上这个值的设定是需要受到来自服务器自身的限制,建议将该值和服务器 CPU 的内核数保存一致。

语法 worker_processes num/auto;
默认值 1
位置 全局块

设定 Nginx 是否以守护进程的方式启动。

语法 daemon on|off;
默认值 daemon on;
位置 全局块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
master_process on|off; # 是否开启工作进程,default on

worker_process num|auto; # 建议设置的和服务器cpu内核个数保持一致,生成工作进程的数量

daemon on|off # 是否以守护进程启动,default on

pid /usr/local/nginx/logs # nginx.pid位置

error_log /usr/local/nginx/logs/ error # error_logs 可以配置在全局,http,server,location

# 其中日志级别的值有:debug|info|notice|warn|error|crit|alert|emerg,这块建议大家设置的时候不要设置成info以下的等级,因为会带来大量的磁盘I/O消耗,影响Nginx的性能。

include /usr/local/nginx/conf/aaa.conf # 引入其他配置,可以在任何位置配置


# events 块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
默认值accept_mutex on;
accept_mutex on; # 解决惊群,用来设置nginx网络链接序列化,一个个唤醒worker 将会对多个Nginx进程接收连接进行序列号,一个个来唤醒接收,就防止了多个进程对连接的争抢。

默认值multi_accept off;
multi_accept on; # 是否允许同时接受多个网络链接
如果multi_accept被禁止了,nginx一个工作进程只能同时接受一个新的连接。否则,一个工作进程可以同时接受所有的新连接


worker_connections 512; # 单个worker进程最大连接数,number值不能大于操作系统支持打开的最大文件句柄数量。

use epoll|select|poll|kqueue; # 来设置Nginx服务器选择哪种事件驱动来处理网络消息。建议epoll

events {
accept_mutex on;
multi_accept on;
use epoll;
worker_connections 1024;
}

# http 块

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# mime-type Nginx作为web服务器,也需要能够识别前端请求的资源类型。
Nginx的配置文件中,默认有两行配置
include mime.types;
default_type application/octet-stream; # http,server,location 可以配置,默认二进制流,浏览器会下载文件

| 语法 | default_type mime-type; |
| ------ | ------------------------- |
| 默认值 | default_type text/plain; |


location /get_text {
default_type text/html;
return 200 "<h1>shabi</h1>";
}

sendfile on; # 配置位置 http,server,location

keepalive_timeout 75 # 长连接超时协议

keepalive_requests 100 # 长连接处理请求个数

# 定义日志

1
2
3
4
5
6
7
Nginx中日志的类型分access.log、error.log

access.log:用来记录用户所有的访问请求。

error.log:记录nginx本身运行时的错误信息,不会记录用户的访问请求。

Nginx服务器支持对服务日志的格式、大小、输出等进行设置,需要使用到两个指令,分别是access_log和log_format指令。
语法 access_log path[format[buffer=size]]
默认值 access_log logs/access.log combined;
位置 http , server , location
语法 log_format name [escape=default|json|none] string…;
------ --------------------------------------------------------
默认值 log_format combined “…”;
位置 http
1
2
access_log /usr/local/nginx/logs hahaha;   # http,server,location块配置
log_format hahaha 'fomat: $http_user_agent';
1
2
3
4
5
6
7
8
9
10
11
12
sendfile:用来设置Nginx服务器是否使用sendfile()传输文件,该属性可以大大提高Nginx处理静态资源的性能
默认值sendfile off;
位置http、server、location

# 使用keepalive模式,可以告诉服务器端在处理完一个请求后保持这个TCP连接的打开状态,若接收到来自这个客户端的其他请求,服务端就会利用这个未被关闭的连接,而不需要重新创建一个新连接,提升效率,但是这个连接也不能一直保持,这样的话,连接如果过多,也会是服务端的性能下降,这个时候就需要我们进行设置其的超时时间。
keepalive_timeout:用来设置长连接的超时时间。
默认值keepalive_timeout 75s;
位置http、server、location

keepalive_requests:用来设置一个keep-alive连接使用的次数。
默认值keepalive_requests 100;
位置http、server、location

# server、location

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 80;
server_name localhost;
location / {
root html;
index index.html index.htm;
}

error_page 500 502 503 504 404 /50x.html;
location = /50x.html {
root html;
}
}

http.server,location 中都有的,location 优先生效

# exercise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
(1)有如下访问:
http://192.168.200.133:8081/server1/location1
访问的是:index_sr1_location1.html
http://192.168.200.133:8081/server1/location2
访问的是:index_sr1_location2.html
http://192.168.200.133:8082/server2/location1
访问的是:index_sr2_location1.html
http://192.168.200.133:8082/server2/location2
访问的是:index_sr2_location2.html
(2)如果访问的资源不存在,
返回自定义的404页面
(3)将/server1和/server2的配置使用不同的配置文件分割
将文件放到/home/www/conf.d目录下,然后使用include进行合并
(4)为/server1和/server2各自创建一个访问日志文件
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
# /home/www/conf.d/haha1.conf
server {
listen 8081;
server_name localhost;
access_log /usr/local/nginx/logs main1;
location server1/location1 {
root /home/www/myweb;
index index_sr1_location1.html index_sr1_location1.htm;
}
location server1/location2 {
root /home/www/myweb;
index index_sr1_location2.html index_sr1_location2.htm;
}

error_page 500 502 503 504 404 /50x.html;
location = /50x.html {
root /home/www/myweb;
index 404.html;
}
}

# /home/www/conf.d/haha2.conf
server {
listen 8082;
server_name localhost;
access_log /usr/local/nginx/logs main2;
location server2/location2 {
root html;
index index_sr2_location2.html index_sr2_location2.htm;
}

error_page 500 502 503 504 404 /50x.html;
location = /50x.html {
root /home/www/myweb;
index 404.html;
}
}
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
worker_processes  1;
user www;
error_log logs/error.log;
pid logs/nginx.pid;
daemon on;

events {
accept_mutex on;
multi_accept on;
use epoll;
worker_connections 1024;
}


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

sendfile on;

keepalive_timeout 65;
keepalive_requests 100;

log_format main1 '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

log_format main2 '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';



resolver 8.8.8.8;

include /home/www/conf.d/*.conf;

}

# 配置 nginx 为系统服务

1
vim /usr/lib/systemd/system/nginx.service
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Unit]
Description=nginx web service
Documentation=http://nginx.org/en/docs/
After=network.target

[Service]
Type=forking
PIDFile=/usr/local/nginx/logs/nginx.pid
ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/usr/local/nginx/sbin/nginx -s reload
ExecStop=/usr/local/nginx/sbin/nginx -s stop
PrivateTmp=true

[Install]
WantedBy=default.target
1
chmod 755 /usr/lib/systemd/system/nginx.service
1
2
3
4
5
6
启动: systemctl start nginx
停止: systemctl stop nginx
重启: systemctl restart nginx
重新加载配置文件: systemctl reload nginx # 不改变master pid,重启worker
查看nginx状态: systemctl status nginx
开机启动: systemctl enable nginx

# nginx 配置环境变量

1
2
3
4
5
vim /etc/profile
在最后一行添加
export PATH=$PATH:/usr/local/nginx/sbin

source /etc/profile

# nginx 虚拟主机

  • 虚拟 Web 主机指的是在同一台物理服务器中发布多个 Web 站点或应用
  • 独立的网站,独立的项目甚至独立的功能模块都可以使用虚拟主机进行发布
  • Nginx 可以基于不同的 IP、不同的端口以及不同的域名实现不同的虚拟主机
  • 虚拟主机内的资源收到虚拟主机配置文件内容约束,与其他虚拟主机相对独立

配置虚拟主机可以基于端口号也可以基于子域名

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
server {
listen 80;
server_name localhost; # 域名或主机名

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

#error_page 404 /404.html;

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}
server {
listen 88;
server_name a.com; # 域名或主机名

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

#error_page 404 /404.html;

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}

# nginx 静态资源部署

我们所请 求的内容就分为两种类型,一类是静态资源、一类是动态资源。

静态资源即指在服务器端真实存在并且能直接拿来展示的一些文件,比如常见的 html 页面、css 文件、js 文件、图 片、视频等资源;

动态资源即指在服务器端真实存在但是要想获取需要经过一定的业务逻辑处理,根据不同的条件展示在页面不同这 一部分内容,比如说报表数据展示、根据当前登录用户展示相关具体数据等资源;

1
2
3
4
5
(1)静态资源的配置指令
(2)静态资源的配置优化
(3)静态资源的压缩配置指令
(4)静态资源的缓存处理
(5)静态资源的访问控制,包括跨域问题和防盗链问题

# (1)静态资源的配置指令

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
默认值listen *:80 | *:8000
位置server
listen *:80;
listen 8080; # server 下配置 监听指定端口上的连接
listen 127.0.0.1:8000;
listen 127.0.0.1; # 监听指定IP的所有端口

-------------------------------------------------

# default_server属性是标识符,用来将此虚拟主机设置成默认主机。所谓的默认主机指的是如果没有匹配到对应的address:port,则会默认执行的。如果不指定默认使用的是第一个server。
如果没有匹配成功,使用第一个server响应请求
比如server_name 写的127.0.0.1,但是访问192.168.1.12,会匹配第一个server

如果配置了default_server ,会使用default_server 所在的server响应
listen 8080 default_server;

-------------------------------------------------

server_name
默认值server_name "";
位置server

配置方式1:精确配置
server_name www.baidu.com baidu.com;
如果你没有域名,你就要改hosts啦
匹配方式2:通配符匹配
server_name *.baidu.com;
*不能放在域名中间 ,比如 server_name www.*.com;
*不能在最后,比如 www.baidu.c*
匹配方式三:正则匹配
server_name ~^www\.(\w+)\.com$;
location / {
default_type text/plain;
return 200 '====$1';
# ~后不能有空格
}
# 三种匹配方式都存在,且都能匹配,那么精确匹配 > 前置通配符 > 后置通配符 > 正则匹配 > default_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
# location 用来设置请求的URI
位置server,location
1.不带任何符号
server {
location /abc {
# /abc /abcdef /abc/ /abc?tom=1 都可以被匹配到
default_type text/plain;
return 200 'success';
}
location = /abc {
# 精确匹配 /abc /abc?tom=1
# 不能匹配 /abc/ /abcde
}
location ~^/abc\w$ {
# abc_ 区分大小写
}
# ~ : 用于表示当前uri中包含了正则表达式,并且区分大小写
# ~*: 用于表示当前uri中包含了正则表达式,并且不区分大小写

location ~*^/abc\w$ {
# abc_ 不区分大小写
}
# ^~: 用于不包含正则表达式的uri前,功能和不加符号的一致,唯一不同的是,如果模式匹配,那么就停止搜索其他模式了。
location ^~/abcd {
# 非贪婪
# 不加^ 贪婪匹配
}
}
1
2
3
4
5
6
7
8
9
10
11
# root  path为Nginx服务器接收到请求以后查找资源的根目录路径。
默认值root html;
位置http、server、location
# alias:用来更改location的URI
位置location

/usr/local/nginx/html/images/1.jpg
location /images {
root html; # root处理结果 root路径+location
alias html/images; #alias处理结果 直接使用Alisa路径替换location路径,如果location以/结尾,那么alias也要以/结尾
}
1
2
3
4
5
6
7
8
# 在`/usr/local/nginx/html`目录下创建一个 images目录,并在目录下放入一张图片mv.png图片
location /images {
root /usr/local/nginx/html;
}
location /images {
alias /usr/local/nginx/html/images/;
}
# 访问 http://192.168.200.133/images/mv.png 时,上述效果相同
1
2
3
4
# index
网站的默认页
默认值index index.html;位置http、server、location
index index.html index.htm; 从左往右匹配,一经匹配,立即执行
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# error_page error_page:设置网站的错误页面
# 1.
server {
error_page 404 www.baidu.com;
}

# 2.
error_page 500 404 /50x.html;
location /50x.html {
root html;
}

# 3.不能直接访问的location
error_page 404 @hahaha;
location @hahaha {
default_type text/plain;
return 456 'shabi';
}

# 4.将相应代码更改为另外一个
error_page 404 =200 /50x.html;
location /50x.html {
root html;
}

# 静态资源优化

1
2
3
4
5
6
sendfile on;
tcp_nopush on;
tcp_nodelay on;

sendfile 高效的文件传输,http,server,location

image-20230407163654759 image-20230407163846712
1
2
tcp_nopush   # sendfile 打开才生效 位置http,server,location  提升传输效率,即存满缓冲区再发,不要立即push
tcp_nodelay # keep-alive连接开启才生效 提高网络包传输实时性,即有数据不等缓冲区满立即发 不要延迟
image-20230407164306027

在 linux2.5.9 以后的版本中两者是可以兼容的,三个指令都开启的好处是,sendfile 可以开启高效的文件传输模式,tcp_nopush 开启可以确保在发送到客户端之前数据包已经充分 “填满”, 这大大减少了网络开销,并加快了文件发送的速度。 然后,当它到达最后一个可能因为没有 “填满” 而暂停的数据包时,Nginx 会忽略 tcp_nopush 参数, 然后,tcp_nodelay 强制套接字发送数据。由此可知,TCP_NOPUSH 可以与 TCP_NODELAY 一起设置,它比单独配置 TCP_NODELAY 具有更强的性能。所以我们可以使用如下配置来优化 Nginx 静态资源的处理

1
2
3
sendfile on;
tcp_nopush on;
tcp_nodelay on;

# nginx 静态资源压缩

在 Nginx 的配置文件中可以通过配置 gzip 来对静态资源进行压缩,相关的指令可以配置在 http 块、server 块和 location 块中,Nginx 可以通过

1
2
3
ngx_http_gzip_module模块
ngx_http_gzip_static_module模块
ngx_http_gunzip_module模块

对这些指令进行解析和处理

以下指令都可以在 http,server,location 中配置

1
2
3
4
5
6
7
8
9
gzip on   # 用于开启或者关闭gzip功能 http,server,location
gzip_types mine-type # 默认text/html,对mime类型的资源做压缩,多个值空格分隔,使用*压缩所有,http,server,location.不建议压缩图片视频等资源
gzip_comp_level 1 # 默认1,取值1-9,值越大,压缩等级最高。设置越高,浪费cpu资源越大。建议6
gzip_vary off # 默认off,是否携带“Vary:Accept-Encoding”头域的响应头部。主要是告诉接收方,所发送的数据经过了Gzip压缩处理
gzip_buffers 32 4k # 主要实现的是申请number个每个大小为size的缓冲区空间。
gzip_disable "MSIE [1-6]\."; # 针对不同客户端选择是否启用压缩
gzip_http_version 1.1 # 该指令是指定使用Gzip的HTTP最低版本,该指令一般采用默认值即可。
gzip_min_length 20 # 小于20字节不在压缩,可以加单位。但是使用了Chunk编码动态压缩,该指令将被忽略。建议设置为1K或以上
gzip_proxied off; # 该指令设置是否对服务端返回的结果进行Gzip压缩。
1
2
3
4
5
6
7
8
9
10
gzip_proxied 取值
off - 关闭Nginx服务器对后台服务器返回结果的Gzip压缩
expired - 启用压缩,如果header头中包含 "Expires" 头信息
no-cache - 启用压缩,如果header头中包含 "Cache-Control:no-cache" 头信息
no-store - 启用压缩,如果header头中包含 "Cache-Control:no-store" 头信息
private - 启用压缩,如果header头中包含 "Cache-Control:private" 头信息
no_last_modified - 启用压缩,如果header头中不包含 "Last-Modified" 头信息
no_etag - 启用压缩 ,如果header头中不包含 "ETag" 头信息
auth - 启用压缩 , 如果header头中包含 "Authorization" 头信息
any - 无条件启用压缩

新建 nginx_gzip.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
gzip on;
gzip_types *;
gzip_comp_level 6;
gzip_min_length 1024;
gzip_buffers 4 16K;
gzip_http_version 1.1;
gzip_vary on;
gzip_disable "MSIE [1-6]\.";
gzip_proxied off;


在nginx.conf
include nginx_gzip.conf

gzip 和 sendfile 冲突

gzip 压缩是在用户态完成的,使用 sendfile 之后就不在从内核态到用户态,因此无法同时使用

可以在存储压缩过的文件,直接发送压缩过的文件,客户端解压

可以使用 ngx_http_gzip_static_module 模块的 gzip_static 指令来解决。

1
2
3
gzip_static off;  # 检查与访问资源同名的.gz文件时,response中以gzip相关的header返回.gz文件的内容。
取值 on off always
always 浏览器不支持压缩都会发送,on会先判断
1
2
nginx 编译时默认没有该模块
./confighure --with-http_gzip_static_module

使用该模块后会在 header 中加入 content-Encoding: gzip Vary: Accept-Encoding

# brotil

需要 https 支持

br 和 gzip 可以共存,br 优先级更高

# 安装

  • 官网
    • https://github.com/google/ngx_brotli
    • https://codeload.github.com/google/brotli/tar.gz/refs/tags/v1.0.9
  • 下载 两个项目
  • 解压缩

把 brotli-1.0.9 下所有文件移走

mv ./* ../ngx_brotli-1.0.0rc/deps/brotli/

编译

1
./configure --with-compat --add-dynamic-module=/root/ngx_brotli-1.0.0rc --prefix=/usr/local/nginx/
  • make
  • ngx_http_brotli_filter_module.so ngx_http_brotli_static_module.so 拷贝到 /usr/local/nginx/modules/
  • 复制 nginx 主程序
  • 配置文件中添加
1
2
load_module "/usr/local/nginx/modules/ngx_http_brotli_filter_module.so";
load_module "/usr/local/nginx/modules/ngx_http_brotli_static_module.so";
1
2
3
4
5
6
7
brotli on;
brotli_static on;
brotli_comp_level 6;
brotli_buffers 16 8k;
brotli_min_length 20;
brotli_types text/plain text/css text/javascript application/javascript text/xml application/xml application/xml+rss application/json image/jpeg image/gif image/png;

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
location / {
#gunzip on;
#gzip_static always;
gzip on; #默认关闭
gzip_buffers 16 8k; #内存缓冲16个8k的块缓冲 32 4k 32位 16 8k 64位
gzip_comp_level 6; #压缩等级
gzip_http_version 1.1; #http版本号
gzip_min_length 256; #最小返回数据文件,>这个值才压缩
gzip_proxied any; #只针对反向代理生效,针对上游服务器返回header做条件压缩
gzip_vary on;
gzip_types text/xml text/css text/javascript; #针对那些mine文件压缩
gzip_disable MSIE6 # 针对那些浏览器关闭压缩,最好别搞正则, 对性能消耗太大
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://httpds;
brotli on;
brotli_static on;
brotli_comp_level 6;
brotli_buffers 16 8k;
brotli_min_length 20;
brotli_types text/plain text/css text/javascript application/javascript text/xml application/xml application/xml+rss application/json image/jpeg image/gif image/png;
}

#测试:
curl -H 'accept-encoding:br' -I http://192.168.13.132

# 静态资源缓存处理

缓存种类

客户端缓存

​ 浏览器缓存

服务端缓存

​ Nginx / Redis / Memcached 等

浏览器缓存

是为了节约网络的资源加速浏览,浏览器在用户磁盘上对最近请求过的文档进行存储,当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的阅览.

成本最低的一种缓存实现

减少网络带宽消耗

降低服务器压力

减少网络延迟,加快页面打开速度

header 和缓存相关字段

header 说明
Expires 缓存过期的日期和时间
Cache-Control 设置和缓存相关的配置信息
Last-Modified 请求资源最后修改时间
ETag 请求变量的实体标签的当前值,比如文件的 MD5 值

image-20230417111638213

(1)用户首次通过浏览器发送请求到服务端获取数据,客户端是没有对应的缓存,所以需要发送 request 请求来获取数据;

(2)服务端接收到请求后,获取服务端的数据及服务端缓存的允许后,返回 200 的成功状态码并且在响应头上附上对应资源以及缓存信息;

(3)当用户再次访问相同资源的时候,客户端会在浏览器的缓存目录中查找是否存在响应的缓存文件

(4)如果没有找到对应的缓存文件,则走 (2) 步

(5)如果有缓存文件,接下来对缓存文件是否过期进行判断,过期的判断标准是 (Expires),

(6)如果没有过期,则直接从本地缓存中返回数据进行展示

(7)如果 Expires 过期,接下来需要判断缓存文件是否发生过变化

(8)判断的标准有两个,一个是 ETag (Entity Tag), 一个是 Last-Modified

(9)判断结果是未发生变化,则服务端返回 304,直接从缓存文件中获取数据

(10)如果判断是发生了变化,重新从服务端获取数据,并根据缓存协商 (服务端所设置的是否需要进行缓存数据的设置) 来进行数据缓存。

强缓存和弱缓存的区别在于是否还要向服务端请求

1
2
3
4
5
6
7
8
9
10
11
12
13
expires off;  # expires:该指令用来控制页面缓存的作用。可以通过该指令控制HTTP应答中的“Expires"和”Cache-Control"
expires [modified] time
expires epoch|max|off;

time:可以整数也可以是负数,指定过期时间,如果是负数,Cache-Control则为no-cache,如果为整数或0,则Cache-Control的值为max-age=time;
epoch: 指定Expires的值为'1 January,1970,00:00:01 GMT'(1970-01-01 00:00:00),Cache-Control的值no-cache
max:指定Expires的值为'31 December2037 23:59:59GMT' (2037-12-31 23:59:59) ,Cache-Control的值为10
off:默认不缓存。

location ~ .*\.(html|js|css|png)$ {
expires 1000;
}
off 整数 负数

image-20230410170254546

1
2
3
# add_header指令是用来添加指定的响应头和响应值。
add_header Cache-control no-store
# 也能实现不缓存

缓存响应指令:

1
2
3
4
5
6
7
8
9
Cache-control: must-revalidate
Cache-control: no-cache
Cache-control: no-store
Cache-control: no-transform
Cache-control: public
Cache-control: private
Cache-control: proxy-revalidate
Cache-Control: max-age=<seconds>
Cache-control: s-maxage=<seconds>
指令 说明
must-revalidate 可缓存但必须再向源服务器进行确认
no-cache 缓存前必须确认其有效性
no-store 不缓存请求或响应的任何内容
no-transform 代理不可更改媒体类型
public 可向任意方提供响应的缓存
private 仅向特定用户返回响应
proxy-revalidate 要求中间缓存服务器对缓存的响应有效性再进行确认
max-age=<秒> 响应最大 Age 值
s-maxage=<秒> 公共缓存服务器响应的最大 Age 值

max-age=[秒]:

# nginx 解决跨域问题

同源:协议、域名 (IP)、端口相同即为同源

有两台服务器分别为 A,B, 如果从服务器 A 的页面发送异步请求到服务器 B 获取数据,如果服务器 A 和服务器 B 不满足同源策略,则就会出现跨域问题。

Access-Control-Allow-Origin: 直译过来是允许跨域访问的源地址信息,可以配置多个 (多个用逗号分隔),也可以使用 * 代表所有源

Access-Control-Allow-Methods: 直译过来是允许跨域访问的请求方式,值可以为 GET POST PUT DELETE…, 可以全部设置,也可以根据需要设置,多个用逗号分隔

1
2
3
4
5
6
7
8
9
10
11
# 解决跨域问题
location {
add_header Access-Control-Allow-Origin http://192.168.13.132:80; # * 匹配所有
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE;
}
location /getUser{
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE;
default_type application/json;
return 200 '{"id":1,"name":"TOM","age":18}';
}

# 防盗链

资源盗链指的是此内容不在自己服务器上,而是通过技术手段,绕过别人的限制将别人的内容放到自己页面上最终展示给用户。以此来盗取大网站的空间和流量。

image-20230417112346770

Referer, 当浏览器向 web 服务器发送请求的时候,一般都会带上 Referer, 来告诉浏览器该网页是从哪个页面链接过来的。

在二次请求时会在请求头中加入 referer,第一次访问时是没有的

后台服务器可以根据获取到的这个 Referer 信息来判断是否为自己信任的网站地址,如果是则放行继续访问,如果不是则可以返回 403 (服务端拒绝访问) 的状态信息。

valid_referers:nginx 会通就过查看 referer 自动和 valid_referers 后面的内容进行匹配,如果匹配到了就将 $invalid_referer 变量置 0,如果没有匹配到,则将 $invalid_referer 变量置为 1,匹配的过程中不区分大小写。

语法 valid_referers none|blocked|server_names|string…
默认值
位置 server、location
1
2
3
4
# 防盗链测试
curl 测试防盗链
curl -I 只返回响应头信息
curl -e "referer" -I "URL"
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
none: 如果Header中的Referer为空,允许访问
blocked:在Header中的Referer不为空,但是该值被防火墙或代理进行伪装过,如不带"http://" 、"https://"等协议头的资源允许访问。就是允许“http://”或"https//"以外的请求。
server_names:指定具体的域名或者IP
string: 可以支持正则表达式和*的字符串。如果是正则表达式,需要以`~`开头表示,例如

location ~ .*\.(png|jpg|gif)$ {
vaild_referers none blocked www.g.com;
if ($invalid_referer) {
return 403;
}
root html/images;
}
# 针对目录进行防盗链
location /static {
root html;
vaild_referers none blocked www.g.com;
if ($invalid_referer) {
return 403;
}
}
1
2
3
4
5
6
7
8
9
10
# 检测到盗链时返回403错误码
location ~*/(js|img|css) {
valid_referers none 192.168.13.132;
if ($invalid_referer) {
return 403;
}

root html;
index index.html index.htm;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 检测到盗链时返回指定页面
location ~*/(js|img|css) {
valid_referers none 192.168.13.132;
if ($invalid_referer) {
return 401;
}

root html;
index index.html index.htm;
}

error_page 401 /401.html;
location = /401.html {
root html;
}
# 注意,只有打开图片链接才能看到指定页面
1
2
3
4
5
6
7
8
9
# 检测到盗链时返回指定图片
location ~ .*\.(png|jpg|gif)$ {
vaild_referers none blocked www.g.com;
if ($invalid_referer) {
# return 403;
rewrite ^/ /imgaes/a.png break;
}
root html/images;
}

Referer 的限制比较粗,比如随意加一个 Referer,上面的方式是无法进行限制的

此处我们需要用到 Nginx 的第三方模块 ngx_http_accesskey_module ,第三方模块如何实现盗链

# rewrite

主要的作用是用来实现 URL 的重写。

Nginx 服务器的 Rewrite 功能的实现依赖于 PCRE 的支持,因此在编译安装 Nginx 服务器之前,需要安装 PCRE 库。Nginx 使用的是 ngx_http_rewrite_module 模块来解析和处理 Rewrite 功能的相关配置。

重写和转发的区别:

1
2
3
4
5
地址重写浏览器地址会发生变化而地址转发则不变
一次地址重写会产生两次请求而一次地址转发只会产生一次请求
地址重写到的页面必须是一个完整的路径而地址转发则不需要
地址重写因为是两次请求所以request范围内属性不能传递给新页面而地址转发因为是一次请求所以可以传递值
地址转发速度快于地址重写

Module ngx_http_rewrite_module (nginx.org)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# set  该指令用来设置一个新的变量。
# server、location、if
set $variable value;
variable:变量的名称,该变量名称要用"$"作为变量的第一个字符,且不能与Nginx服务器预设的全局变量同名。
value:变量的值,可以是字符串、其他变量或者变量的组合等。

server {
listen 8890;
server_name localhost;
location /server {
set $name shabi;
set $age 18;
default_type text/plain;
return 200 $name=$age;
}
}

rewrite 常用全局变量

变量 说明
$args 变量中存放了请求 URL 中的请求指令。比如 http://192.168.200.133:8080?arg1=value1&args2=value2 中的 "arg1=value1&arg2=value2",功能和 $query_string 一样
$http_user_agent 变量存储的是用户访问服务的代理信息 (如果通过浏览器访问,记录的是浏览器的相关版本信息)
$host 变量存储的是访问服务器的 server_name 值
$document_uri 变量存储的是当前访问地址的 URI。比如 http://192.168.200.133/server?id=10&name=zhangsan 中的 "/server",功能和 $uri 一样
$document_root 变量存储的是当前请求对应 location 的 root 值,如果未设置,默认指向 Nginx 自带 html 目录所在位置
$content_length 变量存储的是请求头中的 Content-Length 的值
$content_type 变量存储的是请求头中的 Content-Type 的值
$http_cookie 变量存储的是客户端的 cookie 信息,可以通过 add_header Set-Cookie 'cookieName=cookieValue’来添加 cookie 数据
$limit_rate 变量中存储的是 Nginx 服务器对网络连接速率的限制,也就是 Nginx 配置中对 limit_rate 指令设置的值,默认是 0,不限制。
$remote_addr 变量中存储的是客户端的 IP 地址
$remote_port 变量中存储了客户端与服务端建立连接的端口号
$remote_user 变量中存储了客户端的用户名,需要有认证模块才能获取
$scheme 变量中存储了访问协议
$server_addr 变量中存储了服务端的地址
$server_name 变量中存储了客户端请求到达的服务器的名称
$server_port 变量中存储了客户端请求到达服务器的端口号
$server_protocol 变量中存储了客户端请求协议的版本,比如 "HTTP/1.1"
$request_body_file 变量中存储了发给后端服务器的本地文件资源的名称
$request_method 变量中存储了客户端的请求方式,比如 "GET","POST" 等
$request_filename 变量中存储了当前请求的资源文件的路径名
$request_uri 变量中存储了当前请求的 URI,并且携带请求参数,比如 http://192.168.200.133/server?id=10&name=zhangsan 中的 "/server?id=10&name=zhangsan"

# if

使用正则表达式对变量进行匹配,匹配成功返回 true,否则返回 false。变量与正则表达式之间使用 "","*","!","!*" 来连接。

"~" 代表匹配正则表达式过程中区分大小写,

"~*" 代表匹配正则表达式过程中不区分大小写

"!“和”!*" 刚好和上面取相反值,如果匹配上返回 false, 匹配不上返回 true

判断请求的文件是否存在使用 "-f" 和 "!-f",

当使用 "-f" 时,如果请求的文件存在返回 true,不存在返回 false。

当使用 "!f" 时,如果请求文件不存在,但该文件所在目录存在返回 true, 文件和目录都不存在返回 false, 如果文件存在返回 false

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# if  (condition){...}
# server、location

location /if {
set $param '';
default_type text/plain;
if ($param) {
return 200 $param;
}

if ($request_method = POST) {
return 405;
}
if ($http_user_agent ~ Safari) {
return 200;
}
if (!-f $request_filename) {
return $request_filename;
}
return 200 'empty';
}


  1. 判断请求的目录是否存在使用 "-d" 和 "!-d",

    当使用 "-d" 时,如果请求的目录存在,if 返回 true,如果目录不存在则返回 false

    当使用 "!-d" 时,如果请求的目录不存在但该目录的上级目录存在则返回 true,该目录和它上级目录都不存在则返回 false, 如果请求目录存在也返回 false.

  2. 判断请求的目录或者文件是否存在使用 "-e" 和 "!-e"

    当使用 "-e", 如果请求的目录或者文件存在时,if 返回 true, 否则返回 false.

    当使用 "!-e", 如果请求的文件和文件所在路径上的目录都不存在返回 true, 否则返回 false

  3. 判断请求的文件是否可执行使用 "-x" 和 "!-x"

    当使用 "-x", 如果请求的文件可执行,if 返回 true, 否则返回 false

    当使用 "!-x", 如果请求文件不可执行,返回 true, 否则返回 false

# break

该指令用于中断当前相同作用域中的其他 Nginx 配置。与该指令处于同一作用域的 Nginx 配置中,位于它前面的指令配置生效,位于后面的指令配置无效。

1
2
3
4
5
6
7
8
location /testbreak {
if ($args) {
set $username haha;
break;
}
add_header $username 222;
return 200;
}

# return

该指令用于完成对请求的处理,直接向客户端返回响应状态代码。在 return 后的所有 Nginx 配置都是无效的。

1
2
3
return code [text];
return code URL;
return URL;

code: 为返回给客户端的 HTTP 状态代理。可以返回的状态代码为 0~999 的任意 HTTP 状态代理

text: 为返回给客户端的响应体内容,支持变量的使用

URL: 为返回给客户端的 URL 地址

1
2
3
4
5
server /ret {
default_type application/json;
return 200 success;
# return 302 http://www.baidu.com;
}

# rewrite

该指令通过正则表达式的使用来改变 URI。可以同时存在一个或者多个指令,按照顺序依次对 URL 进行匹配和处理。

1
2
3
4
5
6
7
8
# rewrite regex replacement [flag];
# server、location、if

location /rewrite {
rewrite ^/rewrite/url\w*$ https://www.baidu.com;
rewrite ^/rewrite/(test)\w*$; /$1;
rewrite ^/rewrite/(demo)\w*$ /$1;
}

regex: 用来匹配 URI 的正则表达式

replacement: 匹配成功后,用于替换 URI 中被截取内容的字符串。如果该字符串是以 "http://" 或者 "https://" 开头的,则不会继续向下对 URI 进行其他处理,而是直接返回重写后的 URI 给客户端。

flag: 用来设置 rewrite 对 URI 的处理行为,可选值有如下:

​ last: 终止继续在本 location 块中处理接收到的 URI,并将此处重写的 URI 作为一个新的 URI,使用各 location 块进行处理。该标志将重写后的 URI 重写在 server 块中执行,为重写后的 URI 提供了转入到其他 location 块的机会。即本条规则匹配完成后,继续向下匹配新的 1ocation URI 规则

​ break: 将此处重写的 URI 作为一个新的 URI, 在本块中继续进行处理。该标志将重写后的地址在当前的 location 块中执行,不会将新的 URI 转向其他的 location 块。

​ redirect: 将重写后的 URl 返回给客户端,状态码为 302,指明是临时重定向 URl, 主要用在 replacement 变量不是以 "http:/“或者"https:/”" 开头的情况。浏览器地址会显示跳转后的 URL 地址

​ permanent: 将重写后的 URI 返回给客户端,状态码为 301,指明是重定向 URl, 主要用在 replacement 变量不是以 "http://“或者"https://”" 开头的情况。浏览器地址栏会显示跳转后的 URL 地址

# rewrite_log

该指令配置是否开启 URL 重写日志的输出功能。

1
2
3
4
rewrite_log off;
开启后,URL重写的相关日志将以notice级别输出到error_log指令配置的日志文件汇总。
error_log notice;

# rewrite 实现伪静态

1
2
3
4
5
# 假设pageNum为前端接收数据,后端根据对应的pageNum展示对应的页面
<%
String pageNum = request.getParameter("pageNum");
out.println("this is" + pageNum);
%>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
server {
listen 80;
server_name localhost; # 域名或主机名

location / {
rewrite ^/([0-9]+).html$ /index.jsp?pageNum=$1 break;
proxy_pass http://192.168.13.155:8080;
}

#error_page 404 /404.html;

error_page 500 502 503 504 /50x.html;
location = /50x.html {
root html;
}
}

可以实现访问 http://www.a.com/haha?pageNum=13 时客户端显示 http://www.a.com/13.html

# 域名跳转

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 1.修改hosts
127.0.0.1 www.shinya.com
127.0.0.1 www.kbshire.com
127.0.0.1 www.shabi.com

# 2.修改conf
server {
listen 80;
server_name www.shinya.com;
location / {
default_type text/html;
return 200 '<h1>shinya</h1>';
}
}
server {
listen 80;
server_name www.kbshire.com www.shinya.com;
# rewrite ^/ http://www.shinya.com/;
rewrite ^(.*) http://www.shinya.com$1;
}

# 域名镜像

镜像网站指定是将一个完全相同的网站分别放置到几台服务器上,并分别使用独立的 URL 进行访问。其中一台服务器上的网站叫主站,其他的为镜像网站。镜像网站和主站没有太大的区别,可以把镜像网站理解为主站的一个备份节点。

然如果我们不想把整个网站做镜像,只想为其中某一个子目录下的资源做镜像,我们可以在 location 块中配置 rewrite 功能,比如:

提高可用性

加快响应速度

流量负载

1
2
3
4
5
6
7
8
9
10
11
12
# 只对user子目录做镜像
server {
listen 80;
server_name www.shinya.com www.kbshire.com;
location /user {
rewrite ^/user(.*)$ http://www.shabi.com$1;
}
location /emp {
default_type text/html;
return 200;
}
}

# 独立域名

为每个模块设置独立域名

1
2
3
http://search.hm.com  访问商品搜索模块
http://item.hm.com 访问商品详情模块
http://cart.hm.com 访问商品购物车模块
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
server{
listen 80;
server_name search.hm.com;
rewrite ^(.*) http://www.hm.com/bbs$1 last;
}
server{
listen 81;
server_name item.hm.com;
rewrite ^(.*) http://www.hm.com/item$1 last;
}
server{
listen 82;
server_name cart.hm.com;
rewrite ^(.*) http://www.hm.com/cart$1 last;
}

# 自动加 /

重定向的地址会有一个指令叫 server_name_in_redirect on|off; 来决定重定向的地址:

1
2
3
4
如果该指令为on
重定向的地址为: http://server_name/目录名/;
如果该指令为off
重定向的地址为: http://原URL中的域名/目录名/;

server_name_in_redirect on 时,如果访问 192.168.13.155/shabi 会跳转到 localhost/shabi/ 导致无法访问

自动加 / 可以避免自动跳转

注意 server_name_in_redirect 指令在 Nginx 的 0.8.48 版本之前默认都是 on,之后改成了 off, 所以现在我们这个版本不需要考虑这个问题

1
2
3
4
5
6
7
8
9
10
11
12
server {
listen 8082;
server_name localhost;
server_name_in_redirect on;
location /shabi {
root html;
index index.html;
if (-d $request_filename) {
rewrite ^(.*)([^/])$ http://$host:$server_port$1$2/ permanent;
}
}
}

127.0.0.1/shbi 与 127.0.0.1/shabi/ 的区别

127.0.0.1/shabi 不加 / 会 301 重定向到 /shabi/

127.0.0.1/shabi/ 不做重定向

# 目录合并

URL 的目录层级一般不要超过三层,否则的话不利于搜索引擎的搜索也给客户端的输入带来了负担,但是将所有的文件放在一个目录下又会导致文件资源管理混乱并且访问文件的速度也会随着文件增多而慢下来

1
2
3
4
5
6
7
8
9
10
11
server {
listen 8083;
server_name localhost;
location /server {
root html;
index index.html;
rewrite ^/server-([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /server/$1/$2/$3/$4/$5.html last;
# /server/11/22/33/44/20.html
# /server/11-22-33-44-20.html
}
}

# 防盗链

1
2
3
4
5
6
7
8
9
# 检测到盗链时返回指定图片
location ~ .*\.(png|jpg|gif)$ {
vaild_referers none blocked www.g.com;
if ($invalid_referer) {
# return 403;
rewrite ^/ /imgaes/a.png break;
}
root html/images;
}

# Nginx 代理

正向代理

image-20230417115758644

1
2
3
4
5
6
7
8
# 访问192.168.13.155 82 代理到服务器,服务器认为是155访问的
server {
listen 82;
resolver 8.8.8.8; # 设置DNSIP,用来继续proxy_pass中域名
location / {
proxy_pass http://$host$request_uri;
}
}

修稿客户端的代理

# 反向代理

Nginx 反向代理模块的指令是由 ngx_http_proxy_module 模块进行解析,该模块在安装 Nginx 的时候已经自己加装到 Nginx 中了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
proxy_pass # 设置被代理服务器的地址
位置location

server {
listen 8080;
server_name localhost;
location / {
proxy_pass http://192.168.13.155;
}
location /server {
# 此时访问的是/server/index.html
proxy_pass http://192.168.13.155;
}
location /server {
# 此时访问的是/index.html
proxy_pass http://192.168.13.155/;
}
# 注意location / proxy_pass 后面会帮你加上 /
# loction /xxx 后面不加 / 会出现上面的问题
}
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
proxy_set_header 
# 该指令可以更改Nginx服务器接收到的客户端请求的请求头信息,然后将新的请求头发送给代理的服务器
proxy_set_header Host $proxy_host;
proxy_set_header Connection close;

# 被带服务器
server {
listen 8080;
server_name localhost;
location / {
default_type text/html;
return 200 $http_username;
}
}

# 代理服务器
server {
listen 8080;
server_name localhost;
location / {
proxy_pass http://192.168.13.155:8080;
proxy_set_header username HAHA;
}
}

# 实现功能 访问代理服务器的:8080/server 添加username header,在被代服务器回显给客户端
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
# proxy_redirect
该指令是用来重置头信息中的"Location"和"Refresh"的值。
proxy_redirect redirect replacement;
proxy_redirect default;
proxy_redirect off;
位置http、server、location

# 服务端
server {
listen 8082;
server_name localhost;
location / {
root html;
index index.html;
# 这样会暴露服务端真实ip
#if (!-f $request_filename) {
# return 302 http://192.168.13.155/;
#}
}
}

# 代理服务器
server {
listen 8082;
server_name localhost;
location / {
proxy_pass http://192.168.13.155:8082;
proxy_redirect http://192.168.13.155 http://192.168.13.132;
}
}

# proxy_redirect redirect replacement;
redirect:目标,Location的值
replacement:要替换的值

# proxy_redirect default;
location块的uri变量作为replacement,
将proxy_pass变量作为redirect进行替换

当后端三台服务器上内容不一样时的案例:

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
# 如果后端服务器每台上内容不一样
# 服务器1
server {
listen 8083;
server_name 192.168.13.155;
default_type text/plain;
location / {
return 200 '155';
}
}
# 服务器2
server {
listen 8083;
server_name 192.168.13.166;
default_type text/plain;
location / {
return 200 '166';
}
}

# 代理服务器
server {
listen 8083;
server_name localhost;
location / {
proxy_pass http://192.168.13.155:8083/;
}
}
server {
listen 8084;
server_name localhost;
location / {
proxy_pass http://192.168.13.166:8083/;
}
}

# 代理服务器 - 或者
server {
listen 8083;
server_name localhost;
location /server1 {
proxy_pass http://192.168.13.155:8083/;
}
location /server2 {
proxy_pass http://192.168.13.166:8083/;
}
}

# nginx 安全控制

http 请求转变成 https 请求

nginx 添加 ssl 支持

–with-http_ssl_module

ssl 模块下所有的配置都可以在 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
ssl on;   # 开启ssl支持
位置http、server
或者
server {
listen 443 ssl;
}
效果一样~

ssl_certificate ; # 指定pem证书
ssl_certificate_key # 指定pem secret 文件
ssl_session_cache off; # 设置ssl会话缓存

/*
ssl_session_cache 可选项如下
off:禁用会话缓存,客户端不得重复使用会话
none:禁止使用会话缓存,客户端可以重复使用,但是并没有在缓存中存储会话参数
builtin:内置OpenSSL缓存,仅在一个工作进程中使用。
shared:所有工作进程之间共享缓存,缓存的相关信息用name和size来指定
*/

ssl_session_timeout # 设置客户端能够反复使用储存在缓存中的会话参数时间
ssl_ciphers # 允许的密码,必须为openssl支持的格式,可以使用`openssl ciphers`查看openssl支持的格式。
ssl_prefer_server_ciphers on; #该指令指定是否服务器密码优先客户端密码

生成证书

阿里云生成

openssl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
mkdir /root/cert
cd /root/cert
openssl genrsa -des3 -out server.key 2048
openssl req -new -key server.key -out server.csr
cp server.key server.key.org
openssl rsa -in server.key.org -out server.key
openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

[root@localhost cert]# ls -al
total 20
drwxr-xr-x 2 root root 82 Apr 12 19:48 .
dr-xr-x---. 20 root root 4096 Apr 12 19:42 ..
-rw-r--r-- 1 root root 867 Apr 12 19:48 server.crt
-rw-r--r-- 1 root root 708 Apr 12 19:46 server.csr
-rw------- 1 root root 916 Apr 12 19:47 server.key
-rw------- 1 root root 1062 Apr 12 19:47 server.key.org

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    server {
listen 443 ssl;
server_name localhost;

ssl_certificate /root/cert/server.crt;
ssl_certificate_key /root/cert/server.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;
}


}

1
2
3
4
5
6
7
8
# httprewrite到https,因为浏览器www.baidu.com 默认是http
server {
listen 80;
server_name www,baidu.com;
location / {
rewrite ^(.*) https://baidu.com$1;
}
}

# 反向代理系统优化

Buffer 和 cache

1
2
3
4
5
相同点:
两种方式都是用来提供IO吞吐效率,都是用来提升Nginx代理的性能。
不同点:
缓冲主要用来解决不同设备之间数据传递速度不一致导致的性能低的问题,缓冲中的数据一旦此次操作完成后,就可以删除。
缓存主要是备份,将被代理服务器的数据缓存一份到代理服务器,这样的话,客户端再次获取相同数据的时候,就只需要从代理服务器上获取,效率较高,缓存中的数据可以重复使用,只有满足特定条件才会删除.

下面所有的参数都可以在 http,server,location 中配置

1
2
3
4
5
6
proxy_buffering on  :该指令用来开启或者关闭代理服务器的缓冲区;
proxy_buffers 8 4k :该指令用来指定单个连接从代理服务器读取响应的缓存区的个数和大小。
proxy_buffer_size 4k :该指令用来设置从被代理服务器获取的第一部分响应数据的大小。保持与proxy_buffers中的size一致即可,当然也可以更小。
proxy_busy_buffers_size 8k :该指令用来限制同时处于BUSY状态的缓冲总大小
proxy_temp_path path :当缓冲区存满后,仍未被Nginx服务器完全接受,响应数据就会被临时存放在磁盘文件上,该指令设置文件路径,最多只能三层目录
proxy_temp_file_write_size 8k :该指令用来设置磁盘上缓冲文件的大小。

通用配置

1
2
3
4
proxy_buffering on;
proxy_buffer_size 4 32k;
proxy_busy_buffers_size 64k;
proxy_temp_file_write_size 64k;

# 代理 https 请求

需要第三方模块

https://github.com/chobits/ngx_http_proxy_connect_module

配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
server {
listen 3128;

# dns resolver used by forward proxying
resolver 8.8.8.8;

# forward proxy for CONNECT request
proxy_connect;
proxy_connect_allow 443 563;
proxy_connect_connect_timeout 10s;
proxy_connect_read_timeout 10s;
proxy_connect_send_timeout 10s;

# forward proxy for non-CONNECT request
location / {
proxy_pass http://$host;
proxy_set_header Host $host;
}
}

# 负载均衡

系统的扩展分为纵向扩展和横向扩展

纵向扩展是从单机的角度出发,通过增加系统的硬件处理能力来提升服务器的处理能力

云服务资源增加

・整机:IBM、浪潮、DELL、HP 等

・CPU / 主板:更新到主流

・网卡:10G/40G 网卡

・磁盘:SAS (SCSI) HDD(机械)、HHD(混合)、SATA SSD、PCI-e SSD、

MVMe SSD

• SSD

・多副本机制

・系统盘 / 热点数据 / 数据库存储

• HDD

・冷数据存储

横向扩展是通过添加机器来满足大型网站服务的处理能力。

会话管理

・Nginx 高级负载均衡

• ip_hash

・其他 Hash

• hash $cookie_jsessionid;

• hash $request_uri;

・使用 lua 逻辑定向分发

• Redis + SpringSession

image-20230418100400412

应用集群:将同一应用部署到多台机器上,组成处理集群,接收负载均衡设备分发的请求,进行处理并返回响应的数据。

负载均衡器:将用户访问的请求根据对应的负载均衡算法,分发到集群中的一台服务器进行处理。

负载均衡作用:

1、解决服务器的高并发压力,提高应用程序的处理性能。

2、提供故障转移,实现高可用。

3、通过添加或减少服务器数量,增强网站的可扩展性。

4、在负载均衡器上进行过滤,可以提高系统的安全性。

负载均衡常用方式:

用户手动选择,比如下载时选择电信、联通

DNS 轮询 :对同一个主机名添加多条 A 记录,这就是 DNS 轮询,DNS 服务器将解析请求按照 A 记录的顺序,随机分配到不同的 IP 上,这样就能完成简单的负载均衡。DNS 轮询的成本非常低,在一些不重要的服务器,被经常使用。

1. 可靠性低

假设一个域名 DNS 轮询多台服务器,如果其中的一台服务器发生故障,那么所有的访问该服务器的请求将不会有所回应,即使你将该服务器的 IP 从 DNS 中去掉,但是由于各大宽带接入商将众多的 DNS 存放在缓存中,以节省访问时间,导致 DNS 不会实时更新。所以 DNS 轮流上一定程度上解决了负载均衡问题,但是却存在可靠性不高的缺点。

2. 负载均衡不均衡

DNS 负载均衡采用的是简单的轮询负载算法,不能区分服务器的差异,不能反映服务器的当前运行状态,不能做到为性能好的服务器多分配请求,另外本地计算机也会缓存已经解析的域名到 IP 地址的映射,这也会导致使用该 DNS 服务器的用户在一定时间内访问的是同一台 Web 服务器,从而引发 Web 服务器减的负载不均衡。

负载不均衡则会导致某几台服务器负荷很低,而另外几台服务器负荷确很高,处理请求的速度慢,配置高的服务器分配到的请求少,而配置低的服务器分配到的请求多。

四层 / 七层负载均衡

# 四层负载均衡 / 七层负载均衡

所谓四层负载均衡指的是 OSI 七层模型中的传输层,主要是基于 IP+PORT 的负载均衡

1
2
3
实现四层负载均衡的方式:
硬件:F5 BIG-IP、Radware等
软件:LVS、Nginx、Hayproxy等

所谓的七层负载均衡指的是在应用层,主要是基于虚拟的 URL 或主机 IP 的负载均衡

1
2
实现七层负载均衡的方式:
软件:Nginx、Hayproxy等

四层和七层负载均衡的区别

1
2
四层负载均衡数据包是在底层就进行了分发,而七层负载均衡数据包则在最顶端进行分发,所以四层负载均衡的效率比七层负载均衡的要高。
四层负载均衡不识别域名,而七层负载均衡识别域名。

其实还有二层、三层负载均衡,二层是在数据链路层基于 mac 地址来实现负载均衡,三层是在网络层一般采用虚拟 IP 地址的方式实现负载均衡。

实际会采用: 四层负载 (LVS)+ 七层负载 (Nginx)

七层负载均衡

Nginx 的负载均衡是在 Nginx 的反向代理基础上把用户的请求根据指定的算法分发到一组【upstream 虚拟服务池】。

1
2
3
4
5
upstream 该指令是用来定义一组服务器,它们可以是监听不同端口的服务器,并且也可以是同时监听TCP和Unix socket的服务器。服务器可以指定不同的权重,默认为1
位置http

server 该指令用来指定后端服务器的名称和一些参数,可以使用域名、IP、端口或者unix socket
位置upstream

image-20230418101020006

该指令用来指定后端服务器的名称和一些参数,可以使用域名、IP、端口或者 unix socket

1
2
3
4
5
6
7
8
9
10
11
12
upstream backend{
server 192.168.200.146:9091;
server 192.168.200.146:9092;
server 192.168.200.146:9093;
}
server {
listen 8083;
server_name localhost;
location /{
proxy_pass http://backend;
}
}

代理服务器在负责均衡调度中的状态有以下几个:

状态 概述
down 当前的 server 暂时不参与负载均衡,该状态一般会对需要停机维护的服务器进行设置。
backup 预留的备份服务器,当主服务器不可用时,将用来传递请求。
max_fails 允许请求失败的次数,默认为 1
fail_timeout 经过 max_fails 失败后,服务暂停时间,默认是 10 秒
max_conns 限制最大的接收连接数,默认为 0,表示不限制,使用该配置可以根据后端服务器处理请求的并发量来进行设置,防止后端服务器被压垮。

负载均衡策略

Nginx 的 upstream 支持如下六种方式的分配算法,分别是:

算法名称 说明
轮询 默认方式
weight 权重方式,权重数据越大,被分配到请求的几率越大;该权重值,主要是针对实际工作环境中不同的后端服务器硬件配置进行调整的,所有此策略比较适合服务器的硬件配置差别比较大的情况
ip_hash 先 hash,再和后端服务器的数量取余,得到的值就是需要转发到后端的服务器。不适用于局域网项目,即多用户共享 IP 的场景。容易造成流量倾斜。后端服务器宕机导致 session 丢失。适用于中小型项目,快速扩容。 依据 ip 分配方式。能够将某个客户端 IP 的请求通过哈希算法定位到同一台后端服务器上。这样,当来自某一个 IP 的用户在后端 Web 服务器 A 上登录后,在访问该站点的其他 URL,能保证其访问的还是后端 web 服务器 A。ip_hash 指令无法保证后端服务器的负载均衡,可能导致有些后端服务器接收到的请求多,有些后端服务器接收的请求少,而且设置后端服务器权重等方法将不起作用。
least_conn 依据最少连接方式。有些请求占用的时间很长,会导致其所在的后端负载较高。这种情况下,least_conn 这种方式就可以达到更好的负载均衡效果。
url_hash 依据 URL 分配方式。url_hash 按访问 url 的 hash 结果来分配请求,使每个 url 定向到同一个后端服务器,要配合缓存命中来使用。同一个资源多次请求,可能会到达不同的服务器上,导致不必要的多次下载,缓存命中率不高,以及一些资源时间的浪费。而使用 url_hash,可以使得同一个 url(也就是同一个资源请求)会到达同一台服务器,一旦缓存住了资源,再此收到请求,就可以从缓存中读取。
fair 依据响应时间方式。可以根据页面大小、加载时间长短智能的进行负载均衡。
$request_uri 适用于不支持 cookie 的情况下。资源不平均分配:只需要算出 hash 将资源放到对应的服务器
$cookie_jsessionid sessionid 只有 tomcat 才有,并且 hash 的是 value
1
2
3
4
5
6
7
8
9
10
11
12
13
14
upstream backend{
ip_hash;
# hash $request_uri;
server 192.168.200.146:9001 weight=2;
server 192.168.200.146:9002;
server 192.168.200.146:9003;
}
server {
listen 8083;
server_name localhost;
location /{
proxy_pass http://backend;
}
}

fair

fair 采用的不是内建负载均衡使用的轮换的均衡算法,而是可以根据页面大小、加载时间长短智能的进行负载均衡。那么如何使用第三方模块的 fair 负载均衡策略。

1
2
3
4
5
upstream backend {
fair;
}
https://github.com/gnosek/nginx-upstream-fair
./configure --add-module=/root/fair
1
2
3
4
5
6
在Nginx的源码中 src/http/ngx_http_upstream.h,找到ngx_http_upstream_srv_conf_s,在模块中添加添加default_port属性

in_port_t default_port

make
make upgrade
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 实现带有URL重写的负载均衡
upstream backend{
server 192.168.200.146:9001;
server 192.168.200.146:9002;
server 192.168.200.146:9003;
}
server {
listen 80;
server_name localhost;
location /file/ {
rewrite ^(/file/.*) /server/$1 last;
}
location / {
proxy_pass http://backend;
}
}

# stick 实现 cookie 负载均衡

Sticky 是 nginx 的一个模块,它是基于 cookie 的一种 nginx 的负载均衡解决方案,通过分发和识别 cookie,来使同一个客户端的请求落在同一台服务器上,默认标识名为 route

客户端首次发起访问请求,nginx 接收后,发现请求头没有 cookie,则以轮询方式将请求分发给后端服务器。

后端服务器处理完请求,将响应数据返回给 nginx。

此时 nginx 生成带 route 的 cookie,返回给客户端。route 的值与后端服务器对应,可能是明文,也可能是 md5、sha1 等 Hash 值

客户端接收请求,并保存带 route 的 cookie。

当客户端下一次发送请求时,会带上 route,nginx 根据接收到的 cookie 中的 route 值,转发给对应的后端服务器。

https://bitbucket.org/nginx-goodies/nginx-sticky-module-ng/src/master/

依赖 openssl-devel

1
./configure --prefix=/usr/local/nginx --add-module=/root/nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d

打开 ngx_http_sticky_misc.c 文件

在 12 行添加

1
2
#include <openssl/sha.h>
#include <openssl/md5.h>

备份之前的程序

1
mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old

把编译好的 Nginx 程序替换到原来的目录里

1
cp objs/nginx /usr/local/nginx/sbin/

升级检测

1
make upgrade

检查程序中是否包含新模块

1
nginx -V
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
upstream httpds {
sticky name=shabi expires=6h;
server 192.168.13.155:8080;
server 192.168.13.166:8080;
}

server {
listen 80;
server_name localhost;

#charset koi8-r;

#access_log logs/host.access.log main;

location / {
proxy_pass http://httpds;
}

location ~*/(js|img|css) {
root /usr/local/nginx/html;
}

浏览器多标签会共享 cookie,且名字不好和 JESEEIONID 重复

原理是在 cookie 中加入一个字段,从会话保持,也能维持静态会话

# 四层负载均衡

Nginx 在 1.9 之后,增加了一个 stream 模块,用来实现四层协议的转发、代理、负载均衡等。stream 模块的用法跟 http 的用法类似,允许我们配置一组 TCP 或者 UDP 等协议的监听,然后通过 proxy_pass 来转发我们的请求,通过 upstream 添加多个后端服务,实现负载均衡。

image-20230418102743685

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
--with-stream
# stream指令 该指令提供在其中指定流服务器指令的配置文件上下文。和http指令同级。
# 位置main

# upstream指令 该指令和http的upstream指令是类似的

stream {
upstream backend {
server 192.168.13.155:8888;
server 192.168.13.155:8889;
}
upstream backendtomcat {
server 192.168.13.155:8080;
}
server {
listen 81;
proxy_pass backend;
}
server {
listen 82;
proxy_pass backendtomcat;
}
}

四层和七层都有,四层优先

# 缓存

缓存就是数据交换的缓冲区 (称作:Cache), 当用户要获取数据的时候,会先从缓存中去查询获取数据,如果缓存中有就会直接返回给用户,如果缓存中没有,则会发请求从服务器重新查询数据,将数据返回给用户的同时将数据放入缓存,下次用户就会直接从缓存中获取数据。

场景 作用
操作系统磁盘缓存 减少磁盘机械操作
数据库缓存 减少文件系统的 IO 操作
应用程序缓存 减少对数据库的查询
Web 服务器缓存 减少对应用服务器请求次数
浏览器缓存 减少与后台的交互次数

缓存的优点

​ 1. 减少数据传输,节省网络流量,加快响应速度,提升用户体验;

​ 2. 减轻服务器压力;

​ 3. 提供服务端的高可用性;

缓存的缺点

​ 1. 数据的不一致

​ 2. 增加成本

# 浏览器缓存

# 什么时候可以用缓存?
  1. 不常改变的内容
  2. 过期时间
  3. 针对 post/get 请求都可以
  4. 存储位置
  5. 磁盘使用空间限制
  • deskcache

字面理解是从内存中,其实也是字面的含义,这个资源是直接从内存中拿到的,不会请求服务器一般已经加载过该资源且缓存在了内存当中,当关闭该页面时,此资源就被内存释放掉了,再次重新打开相同页面时不会出现 from memory cache 的情况

  • memorycache

是从磁盘当中取出的,也是在已经在之前的某个时间加载过该资源,不会请求服务器但是此资源不会随着该页面的关闭而释放掉,因为是存在硬盘当中的,下次打开仍会 from disk cache

# Age

是 CDN 添加的属性表示在 CDN 中缓存了多少秒

# via

用来标识 CDN 缓存经历了哪些服务器,缓存是否命中,使用的协议

# 强制缓存与协商缓存

强制缓存:直接从本机读取,不请求服务器

协商缓存:发送请求 header 中携带 Last-Modified,服务器可能会返回 304 Not Modified

# 浏览器强制缓存

# cache-control

http1.1 的规范,使用 max-age 表示文件可以在浏览器中缓存的时间以秒为单位

标记 类型 功能
public 响应头 响应的数据可以被缓存,客户端和代理层都可以缓存
private 响应头 可私有缓存,客户端可以缓存,代理层不能缓存(CDN,proxy_pass)
no-cache 请求头 可以使用本地缓存,但是必须发送请求到服务器回源验证
no-store 请求和响应 应禁用缓存
max-age 请求和响应 文件可以在浏览器中缓存的时间以秒为单位
s-maxage 请求和响应 用户代理层缓存,CDN 下发,当客户端数据过期时会重新校验
max-stale 请求和响应 缓存最大使用时间,如果缓存过期,但还在这个时间范围内则可以使用缓存数据
min-fresh 请求和响应 缓存最小使用时间,
must-revalidate 请求和响应 当缓存过期后,必须回源重新请求资源。比 no-cache 更严格。因为 HTTP 规范是允许客户端在某些特殊情况下直接使用过期缓存的,比如校验请求发送失败的时候。那么带有 must-revalidate 的缓存必须校验,其他条件全部失效。
proxy-revalidate 请求和响应 和 must-revalidate 类似,只对 CDN 这种代理服务器有效,客户端遇到此头,需要回源验证
stale-while-revalidate 响应 表示在指定时间内可以先使用本地缓存,后台进行异步校验
stale-if-error 响应 在指定时间内,重新验证时返回状态码为 5XX 的时候,可以用本地缓存
only-if-cached 响应 那么只使用缓存内容,如果没有缓存 则 504 getway timeout

在浏览器和服务器端验证文件是否过期的时候,浏览器在二次请求的时候会携带 IF-Modified-Since 属性

# Expires

过期时间

1
2
3
4
expires 30s;   #缓存30秒
expires 30m; #缓存30分钟
expires 2h; #缓存2小时
expires 30d; #缓存30天

# 协商缓存

# last-modified
# etag

http1.1 支持

在 HTTP 协议中 If-Modified-Since 和 If-None-Match 分别对应 Last-Modified 和 ETag

Entity Tag 的缩写,中文译过来就是实体标签的意思.

HTTP 中并没有指定如何生成 ETag,哈希是比较理想的选择。

在计算 Etag 的时候,会产生 CPU 的耗费,所以也可以用时间戳,但这样直接使用 Last-Modified 即可。

ETag 用来校验用户请求的资源是否有变化,作用和 lastmodified 很像,区别是 lastmodified 精确到秒,ETag 可以用 hash 算法来生成更精确的比对内容。

当用户首次请求资源的时候返回给用户数据和 200 状态码并生成 ETag,再次请求的时候服务器比对 ETag,没有发生变化的话返回 304

Cache-Control 直接是通过不请求来实现,而 ETag 是会发请求的,只不过服务器根据请求的东西的内容有无变化来判断是否返回请求的资源

# cache-control expires 强制缓存

页面首次打开,直接读取缓存数据,刷新,会向服务器发起请求

# etag lastmodify 协商缓存

没发生变化 返回 304 不发送数据

# 浏览器缓存原则

  • 多级集群负载时 last-modified 必须保持一致

  • 还有一些场景下我们希望禁用浏览器缓存。比如轮训 api 上报数据数据

  • 浏览器缓存很难彻底禁用,大家的做法是加版本号,随机数等方法。

  • 只缓存 200 响应头的数据,像 3XX 这类跳转的页面不需要缓存。

  • 对于 js,css 这类可以缓存很久的数据,可以通过加版本号的方式更新内容

  • 不需要强一致性的数据,可以缓存几秒

  • 异步加载的接口数据,可以使用 ETag 来校验。

  • 在服务器添加 Server 头,有利于排查错误

  • 分为手机 APP 和 Client 以及是否遵循 http 协议

  • 在没有联网的状态下可以展示数据

  • 流量消耗过多

  • 提前下发 避免秒杀时同时下发数据造成流量短时间暴增

  • 兜底数据 在服务器崩溃和网络不可用的时候展示

  • 临时缓存 退出即清理

  • 固定缓存 展示框架这种,可能很长时间不会更新,可用随客户端下发

    • 首页有的时候可以看做是框架 应该禁用缓存,以保证加载的资源都是最新的
  • 父子连接 页面跳转时有一部分内容不需要重新加载,可用从父菜单带过来

  • 预加载 某些逻辑可用判定用户接下来的操作,那么可用异步加载那些资源

  • 漂亮的加载过程 异步加载 先展示框架,然后异步加载内容,避免主线程阻塞

Nginx 是从 0.7.48 版开始提供缓存功能。Nginx 是基于 Proxy Store 来实现的,其原理是把 URL 及相关组合当做 Key, 在使用 MD5 算法对 Key 进行哈希,得到硬盘上对应的哈希目录路径,从而将缓存内容保存在该目录中。它可以支持任意 URL 连接,同时也支持 404/301/302 这样的非 200 状态码。Nginx 即可以支持对指定 URL 或者状态码设置过期时间,也可以使用 purge 命令来手动清除指定 URL 的缓存。

Nginx 的 web 缓存服务主要是使用 ngx_http_proxy_module 模块相关指令集来完成

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
# proxy_cache_path 该指定用于设置缓存文件的存放路径
# 位置http
proxy_cache_path /usr/local/proxy_cache keys_zone=itcast:200m levels=1:2 inactive=1d max_size=20g;
再加两层目录,第一层一个字母,第二层两个字母
缓存名称,大小
缓存超时时间,未被访问就会删除
设置最大缓存空间,如果缓存空间存满,默认会覆盖缓存时间最长的资源

# proxy_cache 该指令用来开启或关闭代理缓存,如果是开启则自定使用哪个缓存区来进行缓存。
# 位置http、server、location
proxy_cache itcast
# 这个名字需要和上面定义的keys_zone的名字相同

# proxy_cache_key 该指令用来设置web缓存的key值,Nginx会根据key值MD5哈希存缓存
# 默认值 proxy_cache_key \$scheme\$proxy_host$request_uri;
# 位置http、server、location

# proxy_cache_vaild 该指令用来对不同返回状态码的URL设置不同的缓存时间
# 位置http、server、location
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;
# 为200和302的响应URL设置10分钟缓存,为404的响应URL设置1分钟缓存
proxy_cache_valid any 1m;
# 对所有响应状态码的URL都设置1分钟缓存
# 最小匹配优先

# proxy_cache_min_uses 该指令用来设置资源被访问多少次后被缓存
# 位置http、server、location
默认值为1

# proxy_cache_methdos 该指令用户设置缓存哪些HTTP方法
# 位置http、server、location
默认值为GET HEAD ,支持get head post


# 断点续传缓存 range
当有完整的content-length之后即可断点续传
在反向代理服务器中需向后传递header

proxy_set_header Range $http_range;

image-20230418103348767

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
http{
proxy_cache_path /usr/local/proxy_cache levels=2:1 keys_zone=itcast:200m inactive=1d max_size=20g;
upstream backend{
server 192.168.200.146:8080;
}
server {
listen 8080;
server_name localhost;
location / {
proxy_cache itcast;
# 建议使用默认值$schema$proxy_host$request_uri; 相同的proxy_cache_key会出现问题,即该location下不同uri会返回相同的缓存
proxy_cache_key it;
proxy_cache_min_uses 5;
proxy_cache_valid 200 5d;
proxy_cache_valid 404 30s;
proxy_cache_valid any 1m;
add_header nginx-cache "$upstream_cache_status";
proxy_pass http://backend/js/;
}
}
}

# 删除缓存

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
1.rm -rf /usr/local/proxy_cache/......

2.使用ngx_cache_purge
ngx_cache_purge-2.3.tar.gz
./configure --add-module=/root/nginx/module/purge
server {
listen 8080;
server_name localhost;
location / {
proxy_cache xixi;
proxy_cache_key haha;
}
}
server{
location ~/purge(/.*) {
# proxy_cache=name peoxy_cache_key=key 上下两个key需要一样
proxy_cache_purge xixi haha;
}
}

# 设置资源不缓存

比如说对于一些经常发生变化的数据。如果进行缓存的话,就很容易出现用户访问到的数据不是服务器真实的数据。所以对于这些资源我们在缓存的过程中就需要进行过滤,不进行缓存。

1
2
3
4
5
6
7
8
9
# 配置位置http,server,location

#proxy_no_cache
#该指令是用来定义不将数据进行缓存的条件。
proxy_no_cache $cookie_nocache $arg_nocache $arg_comment;

#proxy_cache_bypass
#该指令是用来设置不从缓存中获取数据的条件。
proxy_cache_bypass $cookie_nocache $arg_nocache $arg_comment;

上述两个指令都有一个指定的条件,这个条件可以是多个,并且多个条件中至少有一个不为空且不等于 "0", 则条件满足成立。里面使用到了三个变量,分别是 $cookie_nocache、$arg_nocache、$arg_comment

1
2
3
4
$cookie_nocache
指的是当前请求的cookie中键的名称为nocache对应的值
$arg_nocache$arg_comment
指的是当前请求的参数中属性名为nocache和comment对应的属性值
1
2
3
4
5
6
7
8
9
10
11
server{
listen 8080;
server_name localhost;
location / {
if ($request_uri ~ /.*\.js$){
set $nocache 1;
}
proxy_no_cache $nocache $cookie_nocache $arg_nocache $arg_comment;
proxy_cache_bypass $nocache $cookie_nocache $arg_nocache $arg_comment;
}
}

或者通过 url 指定后面两个参数

/jquery.js?nocache=1

/jquery.js?comment=1

都不会缓存或者使用 set cookie 中的 nocache

上面三个变量只要不是 0 就行

推荐两个都用 proxy_no_cache proxy_cache_bypass

# 动静分离

动:后台应用程序的业务处理

静:网站的静态资源 (html,javaScript,css,images 等文件)

分离:将两者进行分开部署访问,提供用户进行访问。举例说明就是以后所有和静态资源相关的内容都交给 Nginx 来部署访问,非静态内容则交个类似于 Tomcat 的服务器来部署访问。

Nginx 在处理静态资源的时候,效率是非常高的,而且 Nginx 的并发访问量也是名列前茅,而 Tomcat 则相对比较弱一些,所以把静态资源交个 Nginx 后,可以减轻 Tomcat 服务器的访问压力并提高静态资源的访问速度。

动静分离以后,降低了动态资源和静态资源的耦合度。如动态资源宕机了也不影响静态资源的展示。

实现动静分离的方式很多,比如静态资源可以部署到 CDN、Nginx 等服务器上,动态资源可以部署到 Tomcat,weblogic 或者 websphere 上。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
# 将后端服务器上的静态资源删除
# 把后端服务器上的静态资源放到nginx上
upstream webservice{
server 192.168.200.146:8080;
}
server {
listen 80;
server_name localhost;

#动态资源
location /demo {
proxy_pass http://webservice;
}
#静态资源
location ~/.*\.(png|jpg|gif|js){
root html/web;
gzip on;
}

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

# keepalived

Keepalived 软件由 C 编写的,最初是专为 LVS 负载均衡软件设计的,Keepalived 软件主要是通过 VRRP 协议实现高可用功能。

1. 选择协议

1
VRRP可以把一个虚拟路由器的责任动态分配到局域网上的 VRRP 路由器中的一台。其中的虚拟路由即Virtual路由是由VRRP路由群组创建的一个不真实存在的路由,这个虚拟路由也是有对应的IP地址。而且VRRP路由1和VRRP路由2之间会有竞争选择,通过选择会产生一个Master路由和一个Backup路由。

2. 路由容错协议

1
Master路由和Backup路由之间会有一个心跳检测,Master会定时告知Backup自己的状态,如果在指定的时间内,Backup没有接收到这个通知内容,Backup就会替代Master成为新的Master。Master路由有一个特权就是虚拟路由和后端服务器都是通过Master进行数据传递交互的,而备份节点则会直接丢弃这些请求和数据,不做处理,只是去监听Master的状态

image-20230418104426216

1
2
3
4
5
6
7
8
9
10
11
12
13
14
步骤1:从官方网站下载keepalived,官网地址https://keepalived.org/
步骤2:将下载的资源上传到服务器
keepalived-2.0.20.tar.gz
步骤3:创建keepalived目录,方便管理资源
mkdir keepalived
步骤4:将压缩文件进行解压缩,解压缩到指定的目录
tar -zxf keepalived-2.0.20.tar.gz -C keepalived/
步骤5:对keepalived进行配置,编译和安装
cd keepalived/keepalived-2.0.20
./configure --sysconf=/etc --prefix=/usr/local
make && make install

或者直接:
yum install keepalived

打开 keepalived.conf 配置文件

这里面会分三部,第一部分是 global 全局配置、第二部分是 vrrp 相关配置、第三部分是 LVS 相关配置。

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
global全局部分:
global_defs {
#通知邮件,当keepalived发送切换时需要发email给具体的邮箱地址
notification_email {
tom@itcast.cn
jerry@itcast.cn
}
#设置发件人的邮箱信息
notification_email_from zhaomin@itcast.cn
#指定smpt服务地址
smtp_server 192.168.200.1
#指定smpt服务连接超时时间
smtp_connect_timeout 30
#运行keepalived服务器的一个标识,可以用作发送邮件的主题信息
router_id LVS_DEVEL

#默认是不跳过检查。检查收到的VRRP通告中的所有地址可能会比较耗时,设置此命令的意思是,如果通告与接收的上一个通告来自相同的master路由器,则不执行检查(跳过检查)
vrrp_skip_check_adv_addr
#严格遵守VRRP协议。
vrrp_strict
#在一个接口发送的两个免费ARP之间的延迟。可以精确到毫秒级。默认是0
vrrp_garp_interval 0
#在一个网卡上每组na消息之间的延迟时间,默认为0
vrrp_gna_interval 0
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
VRRP部分,该部分可以包含以下四个子模块
1. vrrp_script
2. vrrp_sync_group
3. garp_group
4. vrrp_instance
我们会用到第一个和第四个,
#设置keepalived实例的相关信息,VI_1为VRRP实例名称
vrrp_instance VI_1 {
state MASTER #有两个值可选MASTER主 BACKUP备
interface ens33 #vrrp实例绑定的接口,用于发送VRRP包[当前服务器使用的网卡名称]
virtual_router_id 51#指定VRRP实例ID,范围是0-255
priority 100 #指定优先级,优先级高的将成为MASTER
advert_int 1 #指定发送VRRP通告的间隔,单位是秒
authentication { #vrrp之间通信的认证信息
auth_type PASS #指定认证方式。PASS简单密码认证(推荐)
auth_pass 1111 #指定认证使用的密码,最多8位
}
virtual_ipaddress { #虚拟IP地址设置虚拟IP地址,供用户访问使用,可设置多个,一行一个
192.168.200.222
}
}
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 {
tom@itcast.cn
jerry@itcast.cn
}
notification_email_from zhaomin@itcast.cn
smtp_server 192.168.200.1
smtp_connect_timeout 30
router_id keepalived1
vrrp_skip_check_adv_addr
vrrp_strict
vrrp_garp_interval 0
vrrp_gna_interval 0
}

vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 51
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.200.222
}
}
1
keepalived只能做到对网络故障和keepalived本身的监控,即当出现网络故障或者keepalived本身出现问题时,进行切换。但是这些还不够,我们还需要监控keepalived所在服务器上的其他业务,比如Nginx,如果Nginx出现异常了,仅仅keepalived保持正常,是无法完成系统的正常工作的,因此需要根据业务进程的运行状态决定是否需要进行主备切换,这个时候,我们可以通过编写脚本对业务进程进行检测监控。

image-20230418112232876

要想让 vip 进行切换,就必须要把服务器上的 keepalived 进行关闭,而什么时候关闭 keepalived 呢?应该是在 keepalived 所在服务器的 nginx 出现问题后,把 keepalived 关闭掉,就可以让 VIP 执行另外一台服务器,但是现在这所有的操作都是通过手动来完成的,我们如何能让系统自动判断当前服务器的 nginx 是否正确启动,如果没有,要能让 VIP 自动进行 "漂移"

keepalived 只能做到对网络故障和 keepalived 本身的监控,即当出现网络故障或者 keepalived 本身出现问题时,进行切换。但是这些还不够,我们还需要监控 keepalived 所在服务器上的其他业务,比如 Nginx, 如果 Nginx 出现异常了,仅仅 keepalived 保持正常,是无法完成系统的正常工作的,因此需要根据业务进程的运行状态决定是否需要进行主备切换,这个时候,我们可以通过编写脚本对业务进程进行检测监控。

实现步骤:

  1. 在 keepalived 配置文件中添加对应的配置像
1
2
3
4
5
6
vrrp_script 脚本名称
{
script "脚本位置"
interval 3 #执行时间间隔
weight -20 #动态调整vrrp_instance的优先级
}
  1. 编写脚本

ck_nginx.sh

1
2
3
4
5
6
7
8
9
#!/bin/bash
num=`ps -C nginx --no-header | wc -l`
if [ $num -eq 0 ];then
/usr/local/nginx/sbin/nginx
sleep 2
if [ `ps -C nginx --no-header | wc -l` -eq 0 ]; then
killall keepalived
fi
fi
1
chmod 755 ck_nginx.sh
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
vrrp_script ck_nginx {
script "/etc/keepalived/ck_nginx.sh" #执行脚本的位置
interval 2 #执行脚本的周期,秒为单位
weight -20 #权重的计算方式
}
vrrp_instance VI_1 {
state MASTER
interface ens33
virtual_router_id 10
priority 100
advert_int 1
authentication {
auth_type PASS
auth_pass 1111
}
virtual_ipaddress {
192.168.200.111
}
track_script {
ck_nginx
}
}

通常如果 master 服务死掉后 backup 会变成 master,但是当 master 服务又好了的时候 master 此时会抢占 VIP,这样就会发生两次切换对业务繁忙的网站来说是不好的。所以我们要在配置文件加入 nopreempt 非抢占,但是这个参数只能用于 state 为 backup,故我们在用 HA 的时候最好 master 和 backup 的 state 都设置成 backup 让其通过 priority 来竞争。

# 制作下载站点

nginx 使用的是模块 ngx_http_autoindex_module 来实现的,该模块处理以斜杠 ("/") 结尾的请求,并生成目录列表。

nginx 编译的时候会自动加载该模块,但是该模块默认是关闭的,我们需要使用下来指令来完成对应的配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# 以下都可以在http,server,location 配置

# autoindex:启用或禁用目录列表输出
autoindex on|off;

# autoindex_exact_size:对应HTLM格式,指定是否在目录列表展示文件的详细大小
autoindex_exact_size on;

# autoindex_format:设置目录列表的格式
autoindex_format html|xml|json|jsonp;

autoindex_localtime: 对应HTML格式,是否在目录列表上显示时间。
默认为off,显示的文件时间为GMT时间。
改为on后,显示的文件时间为文件的服务器时间
autoindex_localtime on;


1
2
3
4
5
6
7
8
location /download{
root /usr/local;
autoindex on;
autoindex_exact_size on;
autoindex_format html;
autoindex_localtime on;
}

# 用户认证模块

对应系统资源的访问,我们往往需要限制谁能访问,谁不能访问。这块就是我们通常所说的认证部分,认证需要做的就是根据用户输入的用户名和密码来判定用户是否为合法用户,如果是则放行访问,如果不是则拒绝访问。

Nginx 对应用户认证这块是通过 ngx_http_auth_basic_module 模块来实现的,它允许通过使用 "HTTP 基本身份验证" 协议验证用户名和密码来限制对资源的访问。默认情况下 nginx 是已经安装了该模块,如果不需要则使用–without-http_auth_basic_module。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
(1)auth_basic:使用“ HTTP基本认证”协议启用用户名和密码的验证

| 语法 | auth_basic string\|off; |
| ------ | --------------------------------- |
| 默认值 | auth_basic off; |
| 位置 | http,server,location,limit_except |

开启后,服务端会返回401,指定的字符串会返回到客户端,给用户以提示信息,但是不同的浏览器对内容的展示不一致。

2)auth_basic_user_file:指定用户名和密码所在文件

| 语法 | auth_basic_user_file file; |
| ------ | --------------------------------- |
| 默认值 | — |
| 位置 | http,server,location,limit_except |
1
2
3
4
5
6
7
8
9
location /download{
root /usr/local;
autoindex on;
autoindex_exact_size on;
autoindex_format html;
autoindex_localtime on;
auth_basic 'please input your auth';
auth_basic_user_file htpasswd;
}

yum install -y httpd-tools

1
2
3
4
htpasswd -c /usr/local/nginx/conf/htpasswd username //创建一个新文件记录用户名和密码
htpasswd -b /usr/local/nginx/conf/htpasswd username password //在指定文件新增一个用户名和密码
htpasswd -D /usr/local/nginx/conf/htpasswd username //从指定文件删除一个用户信息
htpasswd -v /usr/local/nginx/conf/htpasswd username //验证用户名和密码是否正确

13-Nginx 用户认证模块的使用_哔哩哔哩_bilibili

# keepalive

需要持续获取请求,通过 http 连接,则开启 keepalive

1
2
3
4
5
6
keepalive_timeout  65;
65指活跃时间,一旦活跃就会被重置

请求很多但来自不同客户端,就没有必要开启keepalive,比如时间服务器

=0 关闭keepalive,connection: close 服务器没有启动keepalive

image-20230326145512655

如果一个 connections 中有多个 transactions,那么说明这些 transactions 是复用了这个 connections

对于 keepalive 链接,服务端通过 content-length 区分每一个请求

# 什么时候使用?

明显的预知用户会在当前连接上有下一步操作

复用连接,有效减少握手次数,尤其是 https 建立一次连接开销会更大

# 什么时候不用?

访问内联资源一般用缓存,不需要 keepalive

长时间的 tcp 连接容易导致系统资源无效占用

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
# http块的keepalive

keepalive_disable 禁用某些浏览器的keepalive


keepalive_requests
设置一个keep-alive连接上可以服务的请求的最大数量,当最大请求数量达到时,连接被关闭。默认是100
一个keep alive建立之后,nginx就会为这个连接设置一个计数器,记录这个keep alive的长连接上已经接收并处理的客户端请求的数量。如果达到这个参数设置的最大值时,则nginx会强行关闭这个长连接,逼迫客户端不得不重新建立新的长连接。
QPS太大时,会发现有大量的TIME_WAIT的socket连接


send_timeout 两次向客户端写操作的间隔,超过timeout强制断开连接.服务端向客户端传输数据的超时时间。
系统中 若有耗时操作,超过 send_timeout 强制断开连接。 注意:准备过程中,不是传输过程


keepalive_timeout timeout [header_timeout];
设置keep-alive客户端连接在服务器端保持开启的超时值(默认75s);值为0会禁用keep-alive客户端连接;
可选、在响应的header域中设置一个值“Keep-Alive: timeout=time”;通常可以不用设置;
默认值为75s


一般 keepalive_timeout > send_timeout
keeplive_time keepalive保持时间

配置两个keepalive时会有两个keepalive

1
2
3
4
5
6
7
8
9
10
11
# upstream 块的长连接
keepalive 300;
设置到upstream服务器的空闲keepalive连接的最大数量
当这个数量被突破时,最近使用最少的连接将被关闭
keepalive指令不会限制一个nginx worker进程到upstream服务器连接的总数量

keepalive_timeout
连接保留时间
keepalive_requests
一个tcp复用中可以并发接收的请求个数

1
2
3
4
5
6
7
出现大量TIME_WAIT的情况
1)导致 nginx端出现大量TIME_WAIT的情况有两种:

keepalive_requests设置比较小,高并发下超过此值后nginx会强制关闭和客户端保持的keepalive长连接;(主动关闭连接后导致nginx出现TIME_WAIT)
keepalive设置的比较小(空闲数太小),导致高并发下nginx会频繁出现连接数震荡(超过该值会关闭连接),不停的关闭、开启和后端server保持的keepalive长连接;
2)导致后端server端出现大量TIME_WAIT的情况:
nginx没有打开和后端的长连接,即:没有设置proxy_http_version 1.1;和proxy_set_header Connection “”;从而导致后端server每次关闭连接,高并发下就会出现server端出现大量TIME_WAIT
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
http {
upstream BACKEND {
server 192.168.0.18080 weight=1 max_fails=2 fail_timeout=30s;
server 192.168.0.28080 weight=1 max_fails=2 fail_timeout=30s;
keepalive 300;
}
server {
listen 8080 default_server;
server_name "";
location / {
proxy_pass http://BACKEND;
proxy_set_header Host $Host;
proxy_set_header x-forwarded-for $remote_addr;
proxy_set_header X-Real-IP $remote_addr;
add_header Cache-Control no-store;
add_header Pragma no-cache;
proxy_http_version 1.1; // 这两个最好也设置
proxy_set_header Connection "";
}
}
}

# 对客户端限制

client_body_buffer_size

对客户端请求中的 body 缓冲区大小

默认 32 位 8k 64 位 16k

如果请求体大于配置,则写入临时文件

client_header_buffer_size

设置读取客户端请求体的缓冲区大小。 如果请求体大于缓冲区,则将整个请求体或仅将其部分写入临时文件。 默认 32 位 8K。 64 位平台 16K。

client_max_body_size 1000M;

默认 1m,如果一个请求的大小超过配置的值,会返回 413 (request Entity Too Large) 错误给客户端

将 size 设置为 0 将禁用对客户端请求正文大小的检查。

client_body_timeout

指定客户端与服务端建立连接后发送 request body 的超时时间。如果客户端在指定时间内没有发送任何内容,Nginx 返回 HTTP 408(Request Timed Out)

client_header_timeout

客户端向服务端发送一个完整的 request header 的超时时间。如果客户端在指定时间内没有发送一个完整的 request header,Nginx 返回 HTTP 408(Request Timed Out)。

client_body_temp_path path [ level1 [ level2 [ level3`]]]

在磁盘上客户端的 body 临时缓冲区位置

client_body_in_file_only on;

把 body 写入磁盘文件,请求结束也不会删除

client_body_in_single_buffer

尽量缓冲 body 的时候在内存中使用连续单一缓冲区,在二次开发时使用 $request_body 读取数据时性能会有所提高

client_header_buffer_size

设置读取客户端请求头的缓冲区大小

如果一个请求行或者一个请求头字段不能放入这个缓冲区,那么就会使用 large_client_header_buffers

large_client_header_buffers

默认 8k

# 获取客户端 IP

X-Forwarded-for:将上一层的 remoteaddr 写入到 x-forwarded-for

remote_addr 建立 tcp 连接的时候的 IP,反向代理时一定是 nginx 的 ip,无法伪造

1
2
3
4
5
6
location / {
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header X-Forwarded-For $remote_addr;
proxy_pass http://httpds;
}

如果存在多级代理,除了第一级代理以外直接转发即可,无需在设置 x-forwarded-for
或者在加一个 ip X-Forwarded-For: ip1,ip2

# concat 压缩请求个数

concat 模块压缩请求数,减少并发请求个数

淘宝网即,?? 代表这是一个合并请求

image-20230327181444179

https://www.nginx.com/nginx-wiki/build/dirhtml/modules/concat/

./configure --prefix=/usr/local/nginx --add-module=/root/Downloads/nginx-http-concat/

1
2
3
4
5
6
7
8
9
10
location /static/css/ {
concat on;
concat_max_files 20;
}

location /static/js/ {
concat on;
concat_max_files 30;
}

1
<link href="??font.css,bg.css" rel="stylesheet">

png 的可以一张图片包含多张图,在用切割展示不同的部分

image-20230328173546514

其实就是把 css 文件拼接

image-20230328173731682

# 根据 IP 判断用户位置

云厂商在 DNS 中有实现,也可以在 GEOIP 中自己实现

GEOIP 判断 IP 所属区域,实现 IP 阻断或限制

# 1 下载数据库

官网需注册登录

下载数据库

maxmind.com

# 2 安装依赖

# 官方 git

https://github.com/maxmind/libmaxminddb

下载后执行编译安装之后

1
2
$ echo /usr/local/lib  >> /etc/ld.so.conf.d/local.conf 
$ ldconfig

# Nginx 模块

https://github.com/leev/ngx_http_geoip2_module

1
2
3
4
5
6
geoip2 /root/GeoLite2-ASN_20220524/GeoLite2-ASN.mmdb {
$geoip2_country_code country iso_code;
}
location{
add_header country $geoip2_country_code;
}

# 健康检查

主动检查模式

Nginx 服务端会按照设定的间隔时间主动向后端的 upstream_server 发出检查请求来验证后端的各个 upstream_server 的状态。 如果得到某个服务器失败的返回超过一定次数,比如 3 次就会标记该服务器为异常,就不会将请求转发至该服务器

一般情况下后端服务器需要为这种健康检查专门提供一个低消耗的接口。

被动检查模式

Nginx 在代理请求过程中会自动的监测每个后端服务器对请求的响应状态,如果某个后端服务器对请求的响应状态在短时间内累计一定失败次数时,Nginx 将会标记该服务器异常。就不会转发流量给该服务器。 不过每间隔一段时间 Nginx 还是会转发少量的一些请求给该后端服务器来探测它的返回状态。 以便识别该服务器是否恢复。

后端服务器不需要专门提供健康检查接口,不过这种方式会造成一些用户请求的响应失败,因为 Nginx 需要用一些少量的请求去试探后端的服务是否恢复正常。

重试机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
upstream {
server ... max_fails=5 fail_timeout=10s; # 最大失败次数
# max_fails=5和fail_timeout=10s 表示在单位周期为10s钟内,中达到5次连接失败,那么接将把节点标记为不可用,并等待下一个周期(同样时常为fail_timeout)再一次去请求,判断是否连接是否成功。
}

location {
# Nginx 默认判断失败节点状态以connect refuse和time out状态为准,不以HTTP错误状态进行判断失败,因为HTTP只要能返回状态说明该节点还可以正常连接,所以nginx判断其还是存活状态;
# 除非添加了proxy_next_upstream指令设置对404、502、503、504、500和time out等错误进行转到备机处理,
proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | http_429 | non_idempotent | off ...;

proxy_next_upstream_timeout time; # 失败重试总时间
proxy_next_upstream_tries number; # 总时间内重试次数

}

主动状态检查

tengine 版

https://github.com/yaoweibin/nginx_upstream_check_module

nginx 商业版

http://nginx.org/en/docs/http/ngx_http_upstream_hc_module.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
1.下载安装nginx 1.20.2

2.vi /root/path
内容为:https://github.com/yaoweibin/nginx_upstream_check_module/blob/master/check_1.20.1%2B.patch

3.安装patch

4.进入nginx文件夹
[root@mail nginx-1.20.2]# patch -p1 < /root/path

./configure --prefix=/usr/local/nginx20 --add-module=/root/Downloads/nginx_upstream_check_module-0.4.0
make
make install

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
 upstream backend {

server 192.168.44.102 weight=8 down;
server 192.168.13.155:8080;
server 192.168.13.166:8080;
check interval=3000 rise=2 fall=5 timeout=1000 type=http; #每三秒检测一次,两次正常就up,五次失败就down,超时时间1000ms,使用http
check_http_send "HEAD / HTTP/1.0\r\n\r\n";
check_http_expect_alive http_2xx http_3xx;
}

server {
listen 80;
server_name localhost;

#charset koi8-r;

#access_log logs/host.access.log main;

location /status {
check_status;
access_log off;
}


location / {
proxy_pass http://backend;
root html;
}

./nginx -c /usr/local/nginx20/conf/nginx.conf

image-20230403163536618

# 资源静态化

将原本需要计算的请求缓存成文件放在 nginx 里

并发最高的:列表,详情页

image-20230328175858869

前端合并和后端合并

前端合并:节约服务器资源,消耗请求数

后端合并:ssi

# ssi

通常称为服务器端嵌入,是一种类似于 ASP 的基于服务器的网页制作技术。

一个静态化的页面中,需要嵌入一小块实时变化的内容,。例如首页,大部分的页面内容需要缓存但是用户登录后的个人信息是动态信息,不能缓存。那么如何解决这个” 页面部分缓存” 问题,利用 SSI 就可以解决,在首页的静态页面中嵌入个人信息的动态页,由于是服务器端的嵌入,所以用户浏览的时候都是一个嵌入后的页面。

http://nginx.org/en/docs/http/ngx_http_ssi_module.html

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
# http, server, location

ssi on

开启ssi支持,默认是off

ssi_silent_errors on

默认值是off,开启后在处理SSI文件出错时不输出错误提示:”[an error occurred while processing the directive] ”

ssi_types

默认是ssi_types text/html,所以如果需要htm和html支持,则不需要设置这句,如果需要shtml支持,则需要设置:ssi_types text/shtml

ssi_value_length

限制脚本参数最大长度

ssi_last_modified

是否保留lastmodified


server {
ssi on;
ssi_silent_errors on;
ssi_types text/shtml;
}
1
2
3
<!--#include file="header.html"-->
<h1>我是index</h1>
<!--#include virtual="footer.html"-->

image-20230419112317707

# rsync

https://www.samba.org/ftp/rsync/rsync.html

remote synchronize 是一个远程数据同步工具,可通过 LAN/WAN 快速同步多台主机之间的文件。也可以使用 rsync 同步本地硬盘中的不同目录。
rsync 是用于替代 rcp 的一个工具,rsync 使用所谓的 rsync 算法 进行数据同步,这种算法只传送两个文件的不同部分,而不是每次都整份传送,因此速度相当快。

rsync 基于 inotify 开发

Rsync 有三种模式:

  • 本地模式(类似于 cp 命令)
  • 远程模式(类似于 scp 命令)
  • 守护进程(socket 进程:是 rsync 的重要功能)

# 安装

两端安装

1
yum install -y rsync

#

# 密码文件

创建文件 /etc/rsync.password

内容

1
hello:123

修改权限

1
chmod 600 /etc/rsync.password

修改配置

1
2
auth users = sgg
secrets file = /etc/rsyncd.pwd

# 开机启动

/etc/rc.local 文件中添加

1
rsync --daemon
  • 修改权限

echo “sgg:111” >> /etc/rsyncd.passwd

# 查看远程目录

rsync --list-only 192.168.44.104::www/

# 拉取数据到指定目录

rsync -avz rsync://192.168.44.104:873/www

rsync -avz 192.168.44.104::www/ /root/w

# 使用 SSH 方式

rsync -avzP /usr/local/nginx/html/ root@192.168.44.105:/www/

# 推送

修改配置

1
rsync -avz --password-file=/etc/rsyncd.passwd.client /usr/local/nginx/html/ rsync://sgg@192.168.44.105:/www

--delete 删除目标目录比源目录多余文件

# 实时推送

推送端安装 inotify

依赖

1
yum install -y automake
1
2
3
wget http://github.com/downloads/rvoicilas/inotify-tools/inotify-tools-3.14.tar.gz
./configure --prefix=/usr/local/inotify
make && make install

监控目录

1
2
/usr/local/inotify/bin/inotifywait -mrq --timefmt '%Y-%m-%d %H:%M:%S' --format '%T %w%f %e' -e close_write,modify,delete,create,attrib,move //usr/local/nginx/html/

# 简单自动化脚本

1
2
3
4
5
6
7
8
#!/bin/bash

/usr/local/inotify/bin/inotifywait -mrq --timefmt '%d/%m/%y %H:%M' --format '%T %w%f %e' -e close_write,modify,delete,create,attrib,move //usr/local/nginx/html/ | while read file
do

rsync -az --delete --password-file=/etc/rsyncd.passwd.client /usr/local/nginx/html/ sgg@192.168.44.102::ftp/
done

# nginx + php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
    # 这里新加的
# PHP 脚本请求全部转发到 FastCGI处理. 使用FastCGI协议默认配置.
# Fastcgi服务器和程序(PHP,Python)沟通的协议.
location ~ \.php$ {
# 设置监听端口
fastcgi_pass 127.0.0.1:9000;
# 设置nginx的默认首页文件(上面已经设置过了,可以删除)
fastcgi_index index.php;
# 设置脚本文件请求的路径
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
# 引入fastcgi的配置文件
include fastcgi_params;
}
}

1、nginx 的 worker进程 直接管理每一个请求到 nginx 的网络请求。

2、php-fpm 程序也如同 nginx 一样,需要监听端口,并且有 master 和 worker 进程。worker 进程直接管理每一个 php 进程。

3、关于 fastcgi:fastcgi 是一种进程管理器,管理 cgi 进程。市面上有多种实现了 fastcgi 功能的进程管理器,php-fpm 就是其中的一种。再提一点,php-fpm 作为一种 fast-cgi 进程管理服务,会监听端口, 一般默认监听9000端口,并且是监听本机 ,也就是只接收来自本机的端口请求,所以我们通常输入命令 netstat -nlpt|grep php-fpm 会得到:

tcp 0 0 127.0.0.1:9000 0.0.0.0:* LISTEN 1057/php-fpm
这里的 127.0.0.1:9000 就是监听本机 9000 端口的意思。

4、关于 fastcgi 的配置文件,目前 fastcgi 的配置文件一般放在 nginx.conf 同级目录下,配置文件形式,一般有两种:fastcgi.conf 和 fastcgi_params。不同的 nginx 版本会有不同的配置文件,这两个配置文件有一个非常重要的区别:fastcgi_parames 文件中缺少下列配置:
fastcgi_param SCRIPT_FILENAME fastcgi_script_name;
我们可以打开 fastcgi_parames 文件加上上述行,也可以在要使用配置的地方动态添加。使得该配置生效。

5、 当需要处理php请求时,nginx的worker进程会将请求移交给php-fpm的worker进程进行处理,也就是最开头所说的nginx调用了php,其实严格得讲是nginx间接调用php

Nginx 和 PHP 的配置 - 知乎 (zhihu.com)

php 环境搭建(正确配置 nginx 和 php) - 知乎 (zhihu.com)

Edited on

Give me a cup of [coffee]~( ̄▽ ̄)~*

John Doe WeChat Pay

WeChat Pay

John Doe Alipay

Alipay

John Doe PayPal

PayPal