当前位置: 首页 > news >正文

Nginx 限流

在构建高并发、高可用的分布式系统时,流量控制是不可或缺的一环。Nginx 作为一款高性能的 Web 服务器和反向代理,其强大的限流功能为后端服务提供了坚实的保护。本文将深入探讨 Nginx 限流的原理、配置方法以及在实际项目中的应用,并通过丰富的代码案例,帮助读者更好地理解和掌握 Nginx 限流技术,从而构建更健壮、更稳定的服务。

1. 引言

随着互联网业务的快速发展,系统面临的并发访问压力越来越大。在高并发场景下,如果不对流量进行有效控制,可能会导致以下问题:

  • 系统过载: 大量请求涌入,超出系统处理能力,导致服务响应缓慢甚至崩溃。
  • 资源耗尽: 服务器 CPU、内存、网络带宽等资源被迅速消耗殆尽,影响其他正常服务的运行。
  • 恶意攻击: 恶意用户或爬虫通过高频访问进行攻击,如 DDoS 攻击、刷票等,严重威胁系统安全和稳定性。

为了解决这些问题,限流(Rate Limiting)技术应运而生。限流旨在通过限制单位时间内请求的数量,保护系统免受过载和滥用。Nginx 作为业界广泛使用的反向代理服务器,凭借其卓越的性能和灵活的配置,成为了实现流量控制的理想选择。它能够有效地在请求到达后端服务之前进行过滤和管理,从而确保后端服务的稳定运行,提升整体系统的可用性。

2. Nginx 限流原理

Nginx 实现限流主要依赖于两种经典的流量控制算法:漏桶算法(Leaky Bucket)和令牌桶算法(Token Bucket)。

2.1 漏桶算法 (Leaky Bucket)

漏桶算法提供了一种平滑流量输出的机制,其核心思想是:请求以任意速率进入漏桶,但以固定的速率从漏桶中流出。如果漏桶已满,那么新进入的请求将被丢弃。

工作原理:

  1. 水滴(请求)流入: 所有的请求都像水滴一样流入一个固定容量的“漏桶”。
  2. 水滴流出: 漏桶底部有一个固定大小的“漏洞”,水滴以恒定的速率从漏洞中流出,代表请求被处理。
  3. 溢出丢弃: 如果流入的水滴速度过快,导致漏桶被填满,那么多余的水滴将会溢出并被丢弃,这表示超出限流的请求被拒绝。

特点:

  • 强制平滑输出: 无论输入流量如何波动,输出流量始终保持恒定速率,因此非常适合用于保护后端服务,防止其被突发流量压垮。
  • 丢弃超额请求: 当流量超出处理能力时,直接丢弃请求,保证了系统的稳定性。

Nginx 中的体现:
Nginx 的 limit_req_zone 指令在实现速率限制时,就采用了漏桶算法的思想。它会维护一个请求队列,当请求速率超过预设值时,会将请求放入队列中等待处理。如果队列已满,则直接拒绝请求。

2.2 令牌桶算法 (Token Bucket)

令牌桶算法与漏桶算法类似,但它在处理突发流量方面更具优势。其核心思想是:系统以恒定速率生成令牌并放入一个固定容量的令牌桶中。每个请求在被处理之前,必须从桶中获取一个令牌。如果桶中没有令牌,请求就必须等待或被拒绝。

工作原理:

  1. 令牌生成: 令牌以固定的速率被生成并放入一个固定容量的“令牌桶”中。
  2. 令牌消耗: 每个请求在被处理之前,需要从令牌桶中获取一个令牌。如果桶中没有令牌,请求将无法被处理。
  3. 突发处理: 如果令牌桶中有足够的令牌,系统可以一次性处理多个请求,从而允许一定程度的突发流量。
  4. 桶满丢弃: 如果令牌生成速度快于消耗速度,导致令牌桶被填满,那么多余的令牌将被丢弃。

特点:

  • 允许突发: 桶中积累的令牌可以用于处理短时间内的突发流量,提高了系统的吞吐量。
  • 控制平均速率: 令牌的生成速率决定了请求的平均处理速率。

Nginx 中的体现:
Nginx 的 limit_req 指令中的 burst 参数就体现了令牌桶算法的思想。burst 参数允许在短时间内超出平均速率的请求,这些请求会消耗“桶”中预先积累的令牌。当令牌耗尽时,后续请求才会根据漏桶算法的规则进行排队或拒绝。

两种算法的对比:

特性漏桶算法 (Leaky Bucket)令牌桶算法 (Token Bucket)
核心思想平滑输出流量,强制恒定速率控制平均速率,允许一定程度的突发
处理突发不允许突发,直接丢弃超额请求允许突发,通过消耗积累的令牌
流量控制限制输出速率限制平均输入速率
适用场景严格控制流量,防止系统过载允许短时高并发,但需控制整体速率

Nginx 通过结合这两种算法的优点,提供了灵活且强大的限流能力,既能保证系统的稳定性,又能兼顾一定的突发流量处理需求。

3. Nginx 限流模块与指令

Nginx 主要通过两个核心模块来实现限流功能:ngx_http_limit_req_module 用于请求速率限制,ngx_http_limit_conn_module 用于并发连接数限制。

3.1 ngx_http_limit_req_module (请求速率限制)

该模块使用漏桶算法来限制来自单个 IP 地址的请求速率。通过 limit_req_zonelimit_req 两个指令协同工作,可以实现精准的流量控制。

limit_req_zone

该指令用于定义一个限流区域,通常在 http 块中配置。

语法:

limit_req_zone key zone=name:size rate=rate;
  • key: 定义限流的键。通常使用 $binary_remote_addr,表示基于客户端 IP 地址进行限流。$binary_remote_addr$remote_addr 更节省内存。
  • zone=name:size: 定义限流区域的名称和大小。name 是区域的标识符,size 是用于存储限流信息的内存大小。例如,10m 可以存储约 160,000 个 IP 地址的状态信息。
  • rate=rate: 定义请求速率。单位是 r/s (requests per second) 或 r/m (requests per minute)。例如,10r/s 表示每秒最多处理 10 个请求。

示例:

http {limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
}

这个配置定义了一个名为 mylimit 的限流区域,大小为 10MB,允许每个 IP 地址每秒最多处理 10 个请求。

limit_req

该指令在 serverlocation 块中应用已定义的限流区域。

语法:

limit_req zone=name [burst=number] [nodelay];
  • zone=name: 指定要应用的限流区域名称,与 limit_req_zone 中定义的 name 对应。
  • burst=number (可选): 这是对漏桶算法的扩展,类似于令牌桶。它允许在短时间内超出平均速率的请求。当请求速率超过 rate 时,Nginx 会将这些请求放入一个大小为 burst 的队列中。如果队列已满,则新请求将被拒绝,并返回 503 Service Unavailable
  • nodelay (可选): 通常与 burst 配合使用。如果不设置 nodelay,即使队列中有空位,请求也需要按照 rate 定义的速率被处理,这会导致请求处理存在延迟。设置 nodelay 后,只要队列未满,超出的请求就会被立即处理,而不会排队等待。这可以提高用户体验,但可能会导致后端服务在短时间内承受较大压力。

3.2 ngx_http_limit_conn_module (并发连接限制)

该模块用于限制来自单个 IP 地址的并发连接数。同样,它也通过 limit_conn_zonelimit_conn 两个指令协同工作。

limit_conn_zone

该指令用于定义一个连接限制区域,同样在 http 块中配置。

语法:

limit_conn_zone key zone=name:size;
  • key: 定义连接限制的键,通常也是 $binary_remote_addr
  • zone=name:size: 定义连接限制区域的名称和大小。

示例:

http {limit_conn_zone $binary_remote_addr zone=myconnlimit:10m;
}

这个配置定义了一个名为 myconnlimit 的连接限制区域,大小为 10MB。

limit_conn

该指令在 serverlocation 块中应用已定义的连接限制区域,并设置最大连接数。

语法:

limit_conn zone_name number;
  • zone_name: 指定要应用的连接限制区域名称。
  • number: 允许的最大并发连接数。

示例:

server {location /download/ {limit_conn myconnlimit 5;}
}

这个配置表示,对于 /download/ 目录下的资源,每个 IP 地址最多允许 5 个并发连接。超过此限制的连接将被拒绝。

4. Nginx 限流配置实战

本节将通过具体的 Nginx 配置案例,展示如何应用 ngx_http_limit_req_modulengx_http_limit_conn_module 实现不同场景下的限流。

4.1 请求速率限流示例

限制 IP 每秒请求数

这是最常见的限流场景,限制每个客户端 IP 在单位时间内的请求速率。

# http 块中定义限流区域
http {# 定义一个名为 'per_ip_rate_limit' 的限流区域# $binary_remote_addr 作为 key,表示基于客户端 IP 进行限流# zone=per_ip_rate_limit:10m 表示区域名称为 'per_ip_rate_limit',内存大小为 10MB# rate=1r/s 表示每个 IP 每秒最多允许 1 个请求limit_req_zone $binary_remote_addr zone=per_ip_rate_limit:10m rate=1r/s;server {listen 80;server_name localhost;location / {# 应用限流规则# zone=per_ip_rate_limit 指定使用的限流区域# burst=5 允许突发 5 个请求,即在 1r/s 的基础上,可以额外处理 5 个请求,这些请求会进入队列等待# nodelay 表示队列中的请求不会延迟处理,只要有空闲就立即处理limit_req zone=per_ip_rate_limit burst=5 nodelay;proxy_pass http://your_backend_service;# 当请求被限流时,Nginx 默认返回 503 Service Unavailable# 可以自定义错误页面error_page 503 /503.html;location = /503.html {root html;internal;}}}
}

解释:

  • limit_req_zone $binary_remote_addr zone=per_ip_rate_limit:10m rate=1r/s;:定义了一个名为 per_ip_rate_limit 的限流区域,基于客户端 IP 地址进行限流,每秒允许 1 个请求。10MB 的内存可以存储大约 16 万个活跃 IP 的状态信息。
  • limit_req zone=per_ip_rate_limit burst=5 nodelay;:在 / 路径下应用该限流规则。burst=5 意味着允许在短时间内有 5 个突发请求,这些请求会进入一个队列。nodelay 表示这些突发请求不会被延迟处理,而是立即处理(如果后端服务有能力)。如果队列已满,后续请求将直接返回 503 错误。

针对特定 URL 进行限流

有时我们可能需要对网站的特定敏感接口(如登录、注册、下单等)进行更严格的限流。

http {limit_req_zone $binary_remote_addr zone=login_rate_limit:10m rate=5r/m;server {listen 80;server_name localhost;location /api/login {# 针对 /api/login 路径,每个 IP 每分钟最多 5 个请求,允许突发 3 个limit_req zone=login_rate_limit burst=3 nodelay;proxy_pass http://your_auth_service;}location /api/register {# 针对 /api/register 路径,每个 IP 每分钟最多 2 个请求,不允许突发limit_req zone=login_rate_limit;proxy_pass http://your_user_service;}location / {# 其他路径使用默认的限流规则(如果需要)# limit_req zone=per_ip_rate_limit burst=5 nodelay;proxy_pass http://your_backend_service;}}
}

解释:

  • limit_req_zone $binary_remote_addr zone=login_rate_limit:10m rate=5r/m;:定义了一个针对登录/注册接口的限流区域,每分钟允许 5 个请求。
  • location /api/login:对登录接口应用限流,允许 3 个突发请求。
  • location /api/register:对注册接口应用限流,不设置 burst,意味着严格按照 5r/m 的速率处理,超出即拒绝。

4.2 并发连接限流示例

限制 IP 并发连接数

限制单个客户端 IP 同时与 Nginx 建立的连接数,适用于防止恶意连接或资源耗尽。

http {# 定义一个名为 'per_ip_conn_limit' 的连接限流区域# $binary_remote_addr 作为 key# zone=per_ip_conn_limit:10m 表示区域名称和大小limit_conn_zone $binary_remote_addr zone=per_ip_conn_limit:10m;server {listen 80;server_name localhost;location /download/ {# 限制每个 IP 最多 3 个并发连接limit_conn per_ip_conn_limit 3;proxy_pass http://your_file_server;}location /upload/ {# 限制每个 IP 最多 1 个并发连接,防止上传资源滥用limit_conn per_ip_conn_limit 1;proxy_pass http://your_upload_server;}}
}

解释:

  • limit_conn_zone $binary_remote_addr zone=per_ip_conn_limit:10m;:定义了一个名为 per_ip_conn_limit 的连接限流区域。
  • limit_conn per_ip_conn_limit 3;:在 /download/ 路径下,限制每个 IP 最多 3 个并发连接。当连接数超过 3 时,新的连接请求将被拒绝。
  • limit_conn per_ip_conn_limit 1;:在 /upload/ 路径下,限制每个 IP 最多 1 个并发连接,这对于上传这种可能长时间占用连接的操作非常有用。

4.3 白名单/黑名单配置(可选)

在某些场景下,我们可能希望对特定的 IP 地址不进行限流(白名单),或者对恶意 IP 地址直接拒绝访问(黑名单)。这可以通过 geo 模块和 map 模块结合 limit_reqlimit_conn 来实现。

白名单配置

假设我们希望内部 IP 地址(如 192.168.1.0/24)不受到限流影响。

http {# 定义一个变量 $limit_key,用于控制是否进行限流# 默认值为 1,表示进行限流# 对于白名单中的 IP,设置为 0,表示不进行限流map $binary_remote_addr $limit_key {default 1;192.168.1.0/24 0;# 更多白名单 IP 或 IP 段# 10.0.0.1 0;}# 使用 $limit_key 作为限流的 key# 当 $limit_key 为 0 时,Nginx 不会为该 IP 创建限流状态,从而不进行限流limit_req_zone $limit_key zone=per_ip_rate_limit:10m rate=1r/s;server {listen 80;server_name localhost;location / {# 只有当 $limit_key 不为 0 时才应用限流limit_req zone=per_ip_rate_limit burst=5 nodelay;proxy_pass http://your_backend_service;}}
}

解释:

  • map $binary_remote_addr $limit_key { ... }map 指令根据 $binary_remote_addr 的值,设置 $limit_key 的值。如果客户端 IP 在 192.168.1.0/24 网段内,$limit_key 将被设置为 0,否则为 1
  • limit_req_zone $limit_key zone=per_ip_rate_limit:10m rate=1r/s;:这里将 limit_req_zonekey 设置为 $limit_key。当 $limit_key0 时,Nginx 不会为该请求创建限流状态,从而绕过限流。

黑名单配置

对于已知的恶意 IP 地址,我们可以直接拒绝其访问。

http {# 定义一个变量 $blocked_ip,用于判断是否为黑名单 IPmap $binary_remote_addr $blocked_ip {default 0;1.1.1.1 1;2.2.2.2 1;# 更多黑名单 IP}server {listen 80;server_name localhost;location / {# 如果 $blocked_ip 为 1,则直接返回 403 Forbiddenif ($blocked_ip) {return 403;}proxy_pass http://your_backend_service;}}
}

解释:

  • map $binary_remote_addr $blocked_ip { ... }:如果客户端 IP 在黑名单中,$blocked_ip 将被设置为 1
  • if ($blocked_ip) { return 403; }:在 location 块中,如果 $blocked_ip1,Nginx 将直接返回 403 Forbidden 错误,拒绝该 IP 的访问。

注意: if 指令在 Nginx 配置中应谨慎使用,因为它可能会带来一些意想不到的行为。对于复杂的逻辑,建议使用 map 模块或 ngx_http_geo_module

5. 总结与展望

Nginx 限流是构建高并发、高可用系统的重要组成部分。合理地运用 Nginx 限流,不仅可以有效保护后端服务免受突发流量的冲击和恶意攻击,提升系统的稳定性和可用性,还能优化资源利用率,确保核心业务的正常运行。

随着微服务架构和云原生技术的普及,流量控制将变得更加精细化和智能化。除了 Nginx 提供的传统限流方式,我们可能会看到更多基于服务网格(Service Mesh)的流量管理方案,如 Istio、Envoy 等,它们提供了更强大的流量路由、熔断、限流和可观测性能力。此外,结合 AI 和机器学习的智能限流技术也将成为趋势,能够根据实时流量模式和系统负载动态调整限流策略,实现更灵活、更高效的流量管理。

http://www.dtcms.com/a/298743.html

相关文章:

  • Model Control Protocol 三层架构设计,三种传输方式,完成MCP项目构建实现工具调试,多维度评价指标检测多工具多资源调用的鲁棒性和稳健性
  • MyBatisPlus(一)简介与基本CRUD
  • Logcat日志分析
  • SpringBoot 获取请求参数的常用注解
  • 自由学习记录(73)
  • 地铁逃生
  • 注意力机制的使用说明01
  • RNN模型数学推导过程(笔记)
  • 散列表(哈希表)
  • SQL基础⑮ | 触发器
  • 亚德诺半导体AD8539ARZ-REEL7 超低功耗轨到轨运算放大器,自动归零技术,专为可穿戴设备设计!
  • Python 程序设计讲义(20):选择结构程序设计——双分支结构的简化表示(三元运算符)
  • 【linux】Haproxy七层代理
  • 电子基石:硬件工程师的器件手册 (八) - 栅极驱动IC:功率器件的神经中枢
  • 【自动化运维神器Ansible】Ansible常用模块之Copy模块详解
  • 程序代码篇---卡尔曼滤波与PID的组合应用
  • 2.Linux 网络配置
  • 【PyTorch】图像多分类项目部署
  • python基础:request模块简介与安装、基本使用,如何发送get请求响应数据,response属性与请求头
  • centOS7 yum安装新版本的cmake,cmake3以上怎么安装,一篇文章说明白
  • Java并发编程第十篇(ThreadPoolExecutor线程池组件分析)
  • 无印 v1.6 视频解析去水印工具,支持多个平台
  • Android悬浮窗导致其它应用黑屏问题解决办法
  • RocketMQ 5.3.0 ARM64 架构安装部署指南
  • J2EE模式---数据访问对象模式
  • C语言案例《猜拳游戏》
  • VSCode 报错 Error: listen EACCES: permission denied 0.0.0.0:2288
  • Java 笔记 interface
  • C#入门实战:数字计算与条件判断
  • Web攻防-业务逻辑篇密码找回重定向目标响应包检验流程跳过回显泄露验证枚举