Nginx 常用配置

1.限速 Rate Limiting

基本原理

使用了水池算法,即水池的流入水量代表进入的请求,水池的流出水量代表转发请求给应用程序;当设置了某个水池的容量后,如果在某段时间,流入的水量比较大,超过了流出的水量,将导致水池中的水溢出;溢出的水即代表被拒绝的请求;

实现办法

基本设置

limit_req_zone 表示限速区域

  • 第一个参数表示限速匹配条件的关键字,此处为二进制的IP地址 $binary_remote_addr;
  • 第二个参数表示限速区域名称,此处为 mylimi,冒号后面表示用来存储请求数据的内存空间大小,此处设置为 10MB(每 MB 大约可以存储 16000 个二进制 IP 地址,因此 10 MB 大约可以存储 16万个IP地址);
  • 第三个参数 rate 表示限制的速度,此处为 10r/s,表示每秒10个请求,也即每 100 毫秒 1 个请求;
1
2
3
4
5
6
7
8
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;

server {
location /login/ {
limit_req zone=mylimit; // 在某个路径 location 中定义 zone,表示对当前路径进行限速
proxy_pass http://my_upstream;
}
}

应对突发

当第二个请求到达的时间,距离上一个请求的时间少于100毫秒时,Nginx 将返回 503 的响应;为了解决突发的高峰访问的场景,引入了另外两个控制限速的关键字,分别如下:

1
2
3
4
5
location /login/ {
limit_req zone=mylimit burst=20 nodelay;

proxy_pass http://my_upstream;
}:

burst 表示增加一个等待队列,当下一个请求距离上一个请求少于 100 毫秒时,就先将其放入队列中;此处 burst=20 表示同时最多可以有20个请求在排队;如果某个请求进来时,前面已经 20 个请求在排除,则该请求将被拒绝;

免等待队列

虽然 burst 为突发的访问高峰的请求增加了一个缓冲的机制,但它的缺点是让响应变慢了,因为有些请求,例如队列中的第 20 个请求,将等候 2 秒钟的时间后,再会转发给应用程序进行响应;为了避免等待,引入了 nodelay 关键字,它表示请求到达后,将立即被转发给应用程序进行处理,不需等待,但是仍然会占用队列中的一个等待名额;这意味着如果某个时刻同一个 IP 同时发送 21 个请求,则前面 20 个请求将直接转发给应用程序处理,而第 21 个将被拒绝;队列中占用的名额每 100 毫秒释放一个;

两阶段限速

1
2
3
4
5
6
7
8
9
limit_req_zone $binary_remote_addr zone=ip:10m rate=5r/s;

server {
listen 80;
location / {
limit_req zone=ip burst=12 delay=8;
proxy_pass http://website;
}
}

此处仍然建立了能够应对额外 12 个突发请求的队列,但是增加了 delay 参数,并将值设置为 8,它表示队列中的前 8 个请求使用免等待策略,而剩下的 4 个请求需要等待;此时如果进行第 13 个请求,将被拒绝;

高级设置

白名单
  • 先通过 geo 指令建立了一份白名单,普通请求的 $limit 值被默认设置为为 1,指定 IP 段的请求则被设置为 0 ;
  • 再通过 map 指令将 $limit 值为 1 的请求的 $limit_key 属性值设置为 $binary_remote_addr,将$limit 值为 0 的请求设置为空字符串;
  • 最后在 limit_req_zone 指令中,$limit_key 的值若为空字符串的请求,将被忽略,不会施加限制;
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
geo $limit {
default 1;
10.0.0.0/8 0;
192.168.0.0/24 0;
}

map $limit $limit_key {
0 "";
1 $binary_remote_addr;
}

limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;

server {
location / {
limit_req zone=req_zone burst=10 nodelay;

# ...
}
}
单个路径使用多个 limit_req

当使用多个 limit_req 时,如果一个请求被多个 limit_req 同时匹配到,则最长 delay 时间的那个将生效;如果被任意一个 limit_req 拒绝,则请求将拒绝;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
http {
# ...
limit_req_zone $limit_key zone=req_zone:10m rate=5r/s;
limit_req_zone $binary_remote_addr zone=req_zone_wl:10m rate=15r/s;

server {
# ...
location / {
limit_req zone=req_zone burst=10 nodelay;
limit_req zone=req_zone_wl burst=20 nodelay;
# ...
}
}
}

其他配置项

日志

被延误的请求将记录在 warn 日志中;被拒绝的请求将请求在 error 日志中;

1
2015/06/13 04:20:00 [error] 120315#0: *32086 limiting requests, excess: 1.000 by zone "mylimit", client: 192.168.1.2, server: nginx.com, request: "GET / HTTP/1.0", host: "nginx.com"

但是可以手工指定日志等级,以下示例即为指定日志等级为 warn;

1
2
3
4
5
6
location /login/ {
limit_req zone=mylimit burst=20 nodelay;
limit_req_log_level warn;

proxy_pass http://my_upstream;
}

当请求被拒绝时,默认是返回 503 的错误码,如有需要,可以手工设置,以下示例设置为 444

1
2
3
4
location /login/ {
limit_req zone=login burst=4 nodelay;
limit_req_status 444;
}

如果某个路径需要拒绝所有请求,则可以通过设置 deny all 实现;

1
2
3
location /foo.php {
deny all;
}

Nginx 常用配置
https://ccw1078.github.io/2020/11/12/Nginx 配置/
作者
ccw
发布于
2020年11月12日
许可协议