# nginx限流

Nginx 按请求速率限速模块使用的是<mark style="color:blue;">**漏桶算法**</mark>，即能够强行保证请求的实时处理速度不会超过设置的阈值。

## 相关指令 <a href="#limitreqzone-can-shu-pei-zhi" id="limitreqzone-can-shu-pei-zhi"></a>

### limit\_req\_zone

<pre class="language-nginx"><code class="lang-nginx"><strong>limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
</strong></code></pre>

* **$binary\_remote\_addr** 表示通过客户端 IP 来进行限制，“binary\_”的目的是缩小内存占用量。

  限制的是同一客户端 ip 地址的流量。
* **zone=one:10m** 表示生成一个大小为 10M，名字为 one 的内存区域，用来存储访问的频次信息。
* **rate=1r/s** 表示允许相同标识的客户端的访问频次，这里限制的是每秒 1 次。

### limit\_req

```nginx
limit_req zone=one burst=5 nodelay;
```

* **zone=one** 设置使用哪个配置区域来做限制，与上面 limit\_req\_zone 里的 **zone=one:10m** 对应。
* **burst=5** burst 是爆发的意思，这个配置的意思是设置一个大小为 5 的缓冲区，当有大量请求（爆发）过来时，超过了访问频次限制的请求可以先放到这个缓冲区内。
* **nodelay**，如果添加了该属性，超过访问频次且缓冲区也满了的时候就会直接返回503；如果没有添加，则所有请求会等待排队。

### 示例

```nginx
http {
    limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
    server {
        location /search/ {
            limit_req zone=one burst=5 nodelay;
        }
}        
```

限制特定 UA（比如搜索引擎）的访问：

```nginx
limit_req_zone  $anti_spider  zone=one:10m   rate=10r/s;
limit_req zone=one burst=100 nodelay;
if ($http_user_agent ~* "googlebot|bingbot|Feedfetcher-Google") {
    set $anti_spider $http_user_agent;
}
```

## 实战

### 实例一：限制访问速率 <a href="#shi-li-yi-xian-zhi-fang-wen-su-lv" id="shi-li-yi-xian-zhi-fang-wen-su-lv"></a>

```nginx
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit;
    }
}
```

上述规则限制了每个 IP 访问的速度为 2r/s，并将该规则作用于根目录。

<figure><img src="/files/aaJxdYHDxo7lbRuAmZKG" alt=""><figcaption></figcaption></figure>

如上图所示，使用单个 IP 在 10ms 内发并发送了 6 个请求，只有 1 个成功，剩下的 5 个都被拒绝。

> **设置的速度是 2r/s，为什么只有 1 个成功呢？**
>
> 因为 Nginx 的限流统计是基于毫秒的，2r/s 转换一下就是 500ms 内单个 IP 只允许通过 1 个请求，从501ms 开始才允许通过第二个请求。

### 实例二：burst缓存处理 <a href="#shi-li-er-burst-huan-cun-chu-li" id="shi-li-er-burst-huan-cun-chu-li"></a>

```nginx
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit burst=4;
    }
}
```

上面的配置中加入了 burst=4，意思是每个 key(此处是每个 IP)最多允许 4 个突发请求的到来。

<figure><img src="/files/9uOuOtsX5GdbxHXN1Z1V" alt=""><figcaption></figcaption></figure>

相比实例一成功数增加了 4 个，这与设置的 burst 数目是一致的。

具体处理流程是：1 个请求被立即处理，4 个请求被放到 burst 队列里，另外一个请求被拒绝。**通过burst参数，使得 Nginx 限流具备了缓存处理突发流量的能力。**

<mark style="color:blue;">**burst 的作用是让多余的请求可以先放到队列里，慢慢处理。如果不加 nodelay 参数，队列里的请求不会立即处理，而是按照 rate 设置的速度，以毫秒级精确的速度慢慢处理。**</mark>

### 实例三：nodelay降低排队时间 <a href="#shi-li-san-nodelay-jiang-di-pai-dui-shi-jian" id="shi-li-san-nodelay-jiang-di-pai-dui-shi-jian"></a>

> <mark style="color:blue;">**nodelay 参数允许请求在排队的时候就立即被处理**</mark>，也就是说只要请求能够进入 burst 队列，就会立即被后台 worker 处理，<mark style="color:blue;">**这意味着 burst 设置了 nodelay 时，系统瞬间的 QPS 可能会超过 rate 设置的阈值。**</mark>
>
> nodelay 参数要跟burst一起使用才有作用。

```nginx
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=2r/s;
server { 
    location / { 
        limit_req zone=mylimit burst=4 nodelay;
    }
}
```

单个 IP 10ms 内并发发送 6 个请求，结果如下：

<figure><img src="/files/UJ3GUzIRnmnlfdJPHFxR" alt=""><figcaption></figcaption></figure>

跟实例二相比，请求成功率没变化，但是总体耗时变短了。

* 实例二中，有 4 个请求被放到 burst 队列当中，工作进程每隔 500ms(rate=2r/s)取一个请求进行处理，最后一个请求要排队 2s 才会被处理；
* 实例三中，请求放入队列跟实例二是一样的，但不同的是，队列中的请求同时具有了被处理的资格，所以实例三中的 5 个请求可以说是同时开始被处理的，花费时间自然变短了。

> <mark style="color:orange;">**虽然设置 burst 和nodelay 能够降低突发请求的处理时间，但是长期来看并不会提高吞吐量的上限**</mark>，长期吞吐量的上限是由 rate 决定的，因为 nodelay 只能保证 burst 的请求被立即处理，但 Nginx 会限制队列元素释放的速度


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://bohans.gitbook.io/devops/ngnix/group-1/nginx-xian-liu.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
