Nginx中的并发限制之limit_req和limit_conn和limit_rate 详解

时间:2024-10-16 19:23:11

引言
在高流量场景下,良好的限流和连接控制策略至关重要,以防止服务器过载,确保服务稳定性和高可用性。Nginx 提供了 limit_req 和 limit_conn 和 limit_rate ,用以实现请求频率和并发连接数的限制。
本文将详细介绍这两个模块的生效阶段和生效范围,并提供实际配置示例,解释相关指令的作用。

目前来说在nginx上面我们常见的三种限速操作分别是

限制请求数(request) limit_req

限制连接数(connection)  limit_conn

限制响应速度(rate)   limit_rate 

一、limit_req
功能介绍
limit_req 用于限制客户端请求的频率,以防止单一客户端占用过多服务器资源,提升稳定性。

限制在一段时间内的HTTP请求的数量,主要算法原理就是基于在计算机网络中当带宽是有限时十分常用的漏桶算法。

基本原理就是:以漏桶为例,水从顶部倒入,从底下漏出。这里的几个概念分别是:

漏桶对应我们服务器的带宽或者是处理请求的能力或者是一个队列
水表示客户端发送过来的请求
倒入的水则代表客户端发送给服务器但尚未进行处理的请求,此时请求仍在队列(在桶内)
漏出的水则代表从队列中出来即将发送给服务器端处理的请求,此时请求已经离开了队列(在桶外)
漏桶在一定程度上可以代表服务器的处理能力,请求根据先进先出(FIFO)调度算法等待处理。如果倒入水的速度小于漏水的速度,可以理解为服务器能够处理完所有的请求,此时整体服务表现正常。如果倒入水的速度大于漏水的速度,那么水桶内的水会不断增加直到最后溢出,这种情况下在水桶中的水可以理解为在队列中等待的请求,而溢出的水则表示直接被丢弃不处理的请求。
 

生效阶段
limit_req 在请求处理的“访问阶段(access phase)”生效。它在接收到完整的 HTTP 请求后,即将转发到后端之前进行限流。

生效范围
http:全局范围,作用于所有 server 和 location。
server:作用于特定 server block 内的所有 location。
location:作用于特定 URL 路径的 location。
配置示例和注释

限速配置1

http {
  # 定义一个共享内存区域,用于存储请求状态
  # $binary_remote_addr 是客户端的 IP 地址
  # zone=one:10m 定义名为 "one" 的共享内存区域,大小为 10MB
  # rate=1r/s 限制请求速率为每秒 1 个请求
  limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

  server {
    listen 80;
    server_name example.com;

    location /api/ {
      # 应用请求频率限制配置
      # zone=one 引用名为 "one" 的共享内存区域
      # burst=5 允许短时间内突发最多 5 个请求
      # nodelay 如果设置该参数,突发请求也会立即执行
      limit_req zone=one burst=5 nodelay;

      # 代理到后端服务
      proxy_pass http://backend_service;
    }
  }
}

限速配置2

limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;
 
location / {
    limit_req zone=mylimit burst=5 nodelay;
    limit_req _log_level error;
    limit_req_status 503;
    ·······
}
参数解析
  • limit_req_zone:声明一个限制请求的共享内存区域。
  • limit_req:在指定的区域内启用请求频率限制。
  • burst:允许的突发请求数量。
  • nodelay:不延迟处理突发请求。
  • step1 定义请求限制空间 limit_req_zone $binary_remote_addr zone=mylimit:10m rate=1r/s;

    1. $binary_remote_addr,表明是以remote_addr为限制目标,加上binary是为了压缩内存占用空间

    2. zone=name:size,分配一个以name为名的并且大小为size的内存空间,用来存储访问的频次信息

    3. rate=nr/s, 如rate=1r/s表示同一个IP每秒只允许一个请求通过。

  • step2:定义请求限制 limit_req zone=mylimit burst=5 nodelay;

    1.zone=mylimit,使用step1定义的空间

    2.brust=x,表示设置一个大小为x的缓冲区,超过频次限制的请求先放入这个缓冲区。

    3.nodelay,不延迟处理。

            3.1 delay,前delay个不延迟处理,burst-delay的请求延迟处理,超过burst503

            3.2 nodelay/delay都不填,就按照rate,一个个处理,超过brust直接返回503.

 二、limit_conn
功能介绍
limit_conn 用于限制每个客户端的并发连接数,以防止资源被单一客户端耗尽。

根据特定的key来限制连接的数量,需要注意的是并不是所有的连接都会被算入其中,只有当一个连接的整个请求头被读取并且已经被nginx服务器处理的时候才会算入限制中。

生效阶段
limit_conn 在连接处理的“访问阶段(access phase)”生效。当服务器建立新连接时,立即根据配置进行并发连接限制。

生效范围
  • http:全局范围,作用于所有 server 和 location。
  • server:作用于特定 server block 内的所有 location。
  • location:作用于特定 URL 路径的 location。

 配置示例和注释

http {
  # 定义一个共享内存区域,用于存储连接状态
  # $binary_remote_addr 是客户端的 IP 地址
  # zone=addr:10m 定义名为 "addr" 的共享内存区域,大小为 10MB
  limit_conn_zone $binary_remote_addr zone=addr:10m;

  server {
    listen 80;
    server_name example.com;

    location /api/ {
      # 应用并发连接限制配置
      # addr 引用名为 "addr" 的共享内存区域
      # 10 限制每个客户端最多允许 10 个并发连接
      limit_conn addr 10;

      # 代理到后端服务
      proxy_pass http://backend_service;
    }
  }
}
# 注意 应该在http里面 server 外面 
# $binanry_remote_addr 和$remote_addr 一样 但是 减少空间
limit_conn_zone $binanry_remote_addr zone=conn_zone:1m
 
server{
    ...
    location / {
       # 同一时段 只有1个IP能访问
       limit_conn conn_zone 1;
    }
}
参数解析
  • limit_conn_zone:声明一个限制连接数的共享内存区域。
  • limit_conn:在指定的区域内启用连接数限制。

  三、limit_rate

限制用户和服务器之间传输的字节数,最常用的场景可能就是下载/上传限速。

 # 语法配置
 Syntax: limit_rate_after size;
 Default:    limit_rate_after 0;
 Context:    http, server, location, if in location
 This directive appeared in version 0.8.0.
 ​
 # 示例
 location /flv/ {
     flv;
     limit_rate_after 500k;
     limit_rate       50k;
 }

limit_rate的用法非常简单,后面跟随的rate就是具体限速的阈值,限速指令的生效范围是根据每个连接确定的,例如上面限定每个连接的速率为50k,也就是当客户端发起两个连接的时候,速率就可以变为100k

limit_rate_after允许在传输了一部分数据之后再进行限速,例如上面的配置中就是传输的前500kbyte数据不限速,500k之后再进行限速。

limit_rate的一大特点就是能够使用变量,这就意味着和map指令之类的进行组合就可以实现动态限速功能。

日志和状态设置

limit_conn_log_level
limit_conn_log_level 用于设置当连接被限制时的日志记录级别。

可选值
info:基本信息记录。
notice:详细信息记录。
warn:警告信息记录(推荐)。
error:错误信息记录。
limit_conn_status
limit_conn_status 用于设置当连接被限制时返回的 HTTP 状态码。

常用状态码
503:服务不可用(推荐)。
其他自定义状态码:根据具体需求设置。
完整配置示例和注释

http {
  # 为 limit_req 和 limit_conn 定义共享内存区域
  limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;
  limit_conn_zone $binary_remote_addr zone=addr:10m;

  # 设置限制被触发时的日志级别和返回状态码
  limit_conn_log_level warn;
  limit_conn_status 503;

  server {
    listen 80;
    server_name example.com;

    location /api/ {
      # 应用请求频率限制配置
      limit_req zone=one burst=5 nodelay;

      # 应用并发连接限制配置
      limit_conn addr 10;

      # 代理到后端服务
      proxy_pass http://backend_service;
    }
  }
}

解析与说明
limit_req_zone 和 limit_conn_zone:分别定义请求和连接限制的共享内存区域。
limit_req 和 limit_conn:在指定的区域内启用请求频率和连接数限制。
limit_conn_log_level 和 limit_conn_status:分别设置连接限制触发时的日志级别和返回状态码。
结论
通过 Nginx 的 limit_req 和 limit_conn 和 limit_rate,可以有效实现精确的请求频率和连接数控制。这不仅可以防止恶意请求和流量激增对服务器的冲击,还能提高服务的稳定性和可用性。结合日志级别和状态码设置,可以轻松监控和管理限流情况。

参考:Nginx limit_conn 并发连接数限制模块 | Nginx 教程