📖参考:

概述

流量限制(rate-limiting),是 Nginx 中一个非常实用,却经常被错误理解和错误配置的功能。我们可以用来限制用户在给定时间内 HTTP 请求的数量。

流量限制可以用作安全目的,比如可以减慢暴力密码破解的速率。通过将传入请求的速率限制为真实用户的典型值,并标识目标URL地址(通过日志),还可以用来抵御 DDOS 攻击。更常见的情况,该功能被用来保护上游应用服务器不被同时太多用户请求所压垮。

如何限流

Nginx 的”流量限制”使用漏桶算法(leaky bucket algorithm),该算法在通讯和分组交换计算机网络中广泛使用,用以处理带宽有限时的突发情况。就好比,一个桶口在倒水,桶底在漏水的水桶。如果桶口倒水的速率大于桶底的漏水速率,桶里面的水将会溢出;同样,在请求处理方面,水代表来自客户端的请求,水桶代表根据”先进先出调度算法”(FIFO)等待被处理的请求队列,桶底漏出的水代表离开缓冲区被服务器处理的请求,桶口溢出的水代表被丢弃和不被处理的请求。

限流使用的模块

Nginx 的 limit 模块主要包括:ngx_http_limit_req_modulengx_http_limit_conn_modulengx_stream_limit_conn_module

其中 ngx_http_limit_req_modulengx_http_limit_conn_module 为七层限流,ngx_stream_limit_conn_module为四层限流。

ngx_http_limit_req_module 模块

语法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
# 配置限流区域、桶容量(突发容量,默认0)、是否延迟模式(默认延迟)
Syntax: limit_req zone=name [burst=number] [nodelay | delay=number];
Default: —
Context: http, server, location

# 配置限流KEY、及存放KEY对应信息的共享内存区域大小、固定请求速率;此处指定的KEY是“$binary_remote_addr”表示IP地址;固定请求速率使用rate参数配置,支持10r/s和60r/m,即每秒10个请求和每分钟60个请求,不过最终都会转换为每秒的固定请求速率(10r/s为每100毫秒处理一个请求;60r/m,即每1000毫秒处理一个请求)。
Syntax: limit_req_zone key zone=name:size rate=rate [sync];
Default: —
Context: http

# 配置被限流后返回的状态码,默认返回503
Syntax: limit_req_status code;
Default: limit_req_status 503;
Context: http, server, location

# 配置记录被限流后的日志级别,默认error级别
Syntax: limit_req_log_level info | notice | warn | error;
Default: limit_req_log_level error;
Context: http, server, location

例如:

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

server {
...
limit_req zone=perip burst=5 nodelay;
limit_req zone=perserver burst=10;
}

💡建议:对于大部分部署,我们建议使用 burst 和 nodelay 参数来配置 limit_req 指令。

ngx_http_limit_conn_modul 模块

ngx_http_limit_conn_modul 模块基于 key($binary_remote_addr 或者 server_name),对网络总连接数进行限流。

👀注意:不是所有的网络连接都会被计数器统计,只有被 Nginx 处理的并且已经读取了整个请求头的连接才会被计数器统计。

语法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# 用于配置限流 key 及存放限流 key 对应的共享内存大小
Syntax: limit_conn zone number;
Default: —
Context: http, server, location

# 配置存放 key 的共享内存区域名称和指定 key 的最大连接数
Syntax: limit_conn_zone key zone=name:size;
Default: —
Context: http

# 请求被限流后的日志级别,默认 error 级别;
Syntax: limit_conn_log_level info | notice | warn | error;
Default: limit_conn_log_level error;
Context: http, server, location

# 请求被限流后返回的 http 状态码,默认503;
Syntax: limit_conn_status code;
Default:
limit_conn_status 503;
Context: http, server, location

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
http{
# 针对客户端地址,进行连接数限制
limit_conn_zone $binary_remote_addr zone=perip:10m;
# 针对域名,进行连接数限制
limit_conn_zone $server_name zone=perserver:10m;
limit_conn_log_level error;
limit_conn_status 503;

server {
# 每个IP仅允许发起10个连接
limit_conn perip 10;
# 每个域名仅允许发起100个连接
limit_conn perserver 100;
}
}

ngx_stream_limit_conn_module 模块

ngx_stream_limit_conn_module 模块在 TCP/UDP 会话的 Pre-access 阶段被处理。

语法如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 为指定的共享内存区域设置参数,该区域将保留各种 key 的状态。
Syntax: limit_conn_zone key zone=name:size;
Default: —
Context: stream

# 设置共享内存区域和给定 key 的最大允许连接数
Syntax: limit_conn zone number;
Default: —
Context: stream, server

# 为服务器限制连接数设置日志记录级别。
Syntax: limit_conn_log_level info | notice | warn | error;
Default: limit_conn_log_level error;
Context: stream, server

例如:

1
2
3
4
5
6
7
# key 是一个 $binary_remote_addr 变量设置的客户端 IP 地址。$binary_remote_addr 的大小为 IPv4 地址的 4 个字节或 IPv6 地址的 16 个字节。存储状态在 32 位平台上总是占用 32 或 64 字节,在 64 位平台上占用 64 字节。一兆字节区域可以保留大约 32,000 个 32 字节状态或大约 16,000 个 64 字节状态。如果区域存储耗尽,服务器将关闭连接。
limit_conn_zone $binary_remote_addr zone=addr:10m;

server {
...
limit_conn addr 1; # 每次只允许一个 IP 地址连接
}