{"msg":"操作成功","code":200,"data":{"createBy":"admin","createTime":"2025-06-02 12:01:11","updateBy":"admin","updateTime":"2025-06-20 12:01:11","remark":null,"id":113,"articleTitle":"Nginx（六）流量控制","articleUrl":"nginx_limits","articleThumbnail":"https://www.asumimoe.com/imgfiles/20250618/4b57c2e46f8949e5a30d4e2e9e8a8fd5.png","articleFlag":"1","draftStatus":"1","reprintStatement":"0","articleSummary":"Nginx 是一个高性能的 Web 服务器和反向代理服务器，广泛应用于现代 Web 架构中。除了基本的静态资源处理、负载均衡等功能外，Nginx 还提供了强大的流量控制能力，能够帮助系统在高并发场景下实现稳定的请求处理。","articleContent":"Nginx 是一个高性能的 Web 服务器和反向代理服务器，广泛应用于现代 Web 架构中。除了基本的静态资源处理、负载均衡等功能外，Nginx 还提供了强大的**流量控制能力**，能够帮助系统在高并发场景下实现稳定的请求处理。\n\n本文将详细介绍 Nginx 中常见的流量控制策略，包括限流（Rate Limiting）、连接数限制（Limit Connections）等，并结合实际配置示例说明其使用方法与适用场景。\n\n## 什么是流量控制？\n\n流量控制是通过限制单位时间内客户端可以发送的请求数或连接数，防止后端服务因突发访问压力过大而崩溃，从而保障系统的稳定性和可用性。\n\n在互联网应用中，流量控制常用于以下场景：\n\n- 防止 DDoS 攻击\n- 控制 API 调用频率（如 API 接口限流）\n- 保证核心业务的服务质量（QoS）\n\n## Nginx 流量控制模块\n\nNginx 提供了两个主要的模块来实现流量控制：\n\n1. **`ngx_http_limit_req_module`**：限制请求速率（即每秒请求数），基于漏桶算法。\n2. **`ngx_http_limit_conn_module`**：限制并发连接数。\n\n这两个模块默认编译进大多数 Nginx 安装包中，无需额外安装。\n\n## 请求限流（Rate Limiting）\n\n### 1.配置示例\n\n```nginx\nhttp {\n    # 定义一个名为 \"one\" 的限流区域，使用 $binary_remote_addr 作为键大小为 10MB，平均处理速率为每秒 10 个请求（burst=20 表示突发最多 20 个请求）\n    limit_req_zone $binary_remote_addr zone=one:10m rate=10r/s;\n    limit_req_status 428;\n    server {\n        listen 80;\n\n        location /api/ {\n            # 应用限流规则到该路径\n            limit_req zone=one burst=20 delay=9;\n\n            proxy_pass http://backend;\n        }\n    }\n}\n```\n\n### 2.参数解释\n\n此示例配置创建了一个名为one的共享内存区。使用的预定义键是二进制形式的客户端 IP 地址。共享内存区的大小设置为 10 MB。该区域使用关键字参数设置速率。`limit_req` 指令使用了一个必不可少的关键字参数：`zone`。`zone` 指示了要使用哪个共享内存请求限制区的指令。根据 `limit_req_status` 指令的定义，超过明示速率的请求将返回 428 HTTP 代码。建议设置一个 400 级范围的状态码，因为默认值是 503，这代表服务器有问题，而实际问题是出在客户端方面。\n\n- `limit_req_zone`：定义限流区域，语法如下：\n\n  ```nginx\n  limit_req_zone key zone=name:size rate=rate;\n  ```\n\n  - `key`：通常是 `$binary_remote_addr` 或 `$cookie_jsessionid` 等，用于标识用户。\n  - `zone`：共享内存区域名称和大小。\n  - `rate`：请求速率，单位为 r/s（每秒请求数）或 r/m（每分钟请求数）。\n\n- `limit_req`：在具体 location 中启用限流。\n\n  - `zone`：引用之前定义的限流区域。\n  - `burst`：允许突发请求的数量。\n  - `nodelay`：不延迟处理突发请求，直接响应 503 错误。\n\n  `nodelay` 参数不带值，只允许客户端一次性消耗所有流量突发值；但是必须要先等待足够的时间，直到满足速率限制要求为止，否则所有请求都会被拒绝。在此示例中，如果我们使用 `nodelay`，客户端可以在第一秒消耗 12 个请求，但是必须要在初始请求之后等待 4 秒才能发出另一个请求。delay 关键字参数定义了在不限流的情况下可以预先发出多少请求。在这种情况下，客户端可以毫无延迟地预先发出 9 个请求，接下来的 3 个将受到限制，此后 4 秒内的任何请求都将被拒绝。\n\n## 连接数限流（Limit Connections）\n\n### 1.配置示例\n\n```nginx\nhttp {\n    # 定义一个名为 \"perip\" 的连接数限制区域\n    limit_conn_zone $binary_remote_addr zone=perip:10m;\n    limit_conn_status 429;\n    server {\n        listen 80;\n\n        location /download/ {\n            # 每个 IP 最多同时保持 10 个连接\n            limit_conn perip 10;\n\n            root /data/download;\n        }\n    }\n}\n```\n\n### 2.参数解释\n\n此配置创建了一个名为perip的共享内存区。使用的预定义键是二进制形式的客户端 IP 地址。共享内存区的大小设置为 10 MB。limit_conn_status 定义了连接状态被限制为 429 时的响应，此时表示响应过多。\n\n- `limit_conn_zone`：定义连接数限制区域。\n- `limit_conn`：限制每个 key 的最大连接数。\n\n通过使用键来限制连接数量，您不仅能够防止滥用，而且还能在所有客户端之间公平共享资源。请务必谨慎使用预定义键。正如我们在前面的示例中所示，如果许多用户位于源自同一 IP 地址的同一网络上，例如当在网络地址转换（NAT）后面时，使用 IP 地址是不合理的，这会使整个客户端组受到限制。limit_conn_zone 指令仅在 http 上下文中有效。您可以利用 NGINX 在 http 上下文中任意数量的可用变量来构建一个限制字符串。有一种更简洁的方法是，根据具体的用例利用一个变量（例如会话 cookie）识别应用层的用户。limit_conn_status 默认值为 503 时，代表服务不可用。由于服务可用，我们最好使用 429，而 500 级的响应码表示服务器错误，400 级的响应码表示客户端错误。\n\n---\n\n## 限制带宽\n\n使用 NGINX 的 limit_rate 和 limit_rate_after 指令限制响应客户端的带宽：\n\n```nginx\nlocation /download/ {\n    limit_rate_after 10m;\n    limit_rate 1m;\n}\n```\n\n这个 location 代码块的配置指定，对于前缀为 download 的 URI，向客户端提供响应的速率将在 10 MB 之后被限制为每秒 1 MB。带宽限制是针对每个连接的，因此您可能希望在适用的情况下配合使用连接限制和带宽限制。\n通过限制特定连接的带宽，NGINX 能够以您指定的方式在所有客户端上共享其上传带宽，并且只需 `limit_rate_after` 和 `limit_rate` 这两个指令就可以做到这一点。`limit_rate_after` 指令几乎可以在任何上下文中进行设置，包括 http、server、location 以及location 代码块内的 if。`limit_rate` 指令的适用上下文与 `limit_rate_after` 相同，但是它还可以通过一个名为 $limit_rate 的变量进行设置。`limit_rate_after` 指令规定，在传输指定数量的数据之前不得限制连接的速率。limit_rate 指令指定了给定上下文中的速率限制，默认单位是每秒字节数，但是您也可以设置为 m（兆字节）或 g（千兆字节）。这两条指令的默认值都是 0，表示不对下载速率进行任何限制。此模块允许您以编程方式更改客户端的速率限制。\n\n## 高级用法：动态限流 + Lua 脚本\n\n对于更复杂的限流逻辑（如按用户 ID、API Key 限流），可以使用 Nginx 的 Lua 扩展模块（OpenResty）进行定制化开发：\n\n```nginx\nlocation /api/ {\n    access_by_lua_block {\n        local limiter = require \"resty.limit.req\"\n        local _KEY = ngx.var.arg_api_key or \"default\"\n        local lim, err = limiter.new(\"my_limit_key\", 100, 10)\n        if not lim then\n            ngx.log(ngx.ERR, \"failed to instantiate a req object: \", err)\n            return ngx.exit(500)\n        end\n\n        local delay, err = lim:incoming(_KEY, true)\n        if not delay then\n            if err == \"no memory\" then\n                return ngx.exit(500)\n            end\n            -- 触发限流\n            ngx.exit(503)\n        end\n\n        if delay > 0 then\n            ngx.sleep(delay)\n        end\n    }\n\n    proxy_pass http://backend;\n}\n```\n\n#### 参考资料\n\n- [Nginx 官方文档 - limit_req](https://nginx.org/en/docs/http/ngx_http_limit_req_module.html)\n- [Nginx 官方文档 - limit_conn](https://nginx.org/en/docs/http/ngx_http_limit_conn_module.html)\n\n","categoryId":12,"viewCount":169,"categoryName":"Nginx","author":"球接子","authorAvatar":null,"tagIds":[13,14],"tagNames":["Nginx","中间件"]}}