概述

API 限速的首要意图是操控对 API 的拜访频率和数据运用量,以维护 API 和后端服务的稳定性和可靠性。当接收到大量恳求时,或许导致服务器过载或响应时刻变慢,约束 API 的拜访速率能够避免这种情况的发生。此外,API 限速还能够维护 API 免受歹意进犯,如 DDoS 进犯和暴力进犯。另一个原因是,API 提供者或许想要约束 API 的数据运用量,以保证他们的 API 不被乱用或过度运用。能够经过约束每个用户能够恳求的数据量,到达该意图,以便 API 提供者能够操控服务的本钱和资源运用率。综上所述,API 限速是一种有用的办法,能够保证 API 的稳定性和可靠性,避免 API 被歹意进犯和乱用。Nginx 是当时非常受欢迎的 Web 服务器和反向署理服务器。在高并发、高负载的 Web 场景中,Nginx 的高性能、稳定性和可扩展性优势得到了广泛认可,因而 Nginx 在这些场景下往往是最佳挑选。Nginx 也支撑 HTTP、HTTPS、SMTP、POP3 等多种协议,以及负载均衡、缓存、反向署理、安全操控等多种功用,使得它能够适用于各种不同的 Web 署理场景。

下文叙述如何经过 Nginx 完成 API 限速。

ngx_http_map_module 模块

ngx_http_map_module 模块创建值依靠其它变量的值的变量。

示例装备

map $http_host $name {
    hostnames;
    default       0;
    example.com   1;
    *.example.com 1;
    example.org   2;
    *.example.org 2;
    .example.net  3;
    wap.*         4;
}
map $http_user_agent $mobile {
    default       0;
    "~Opera Mini" 1;
}

指令

map

创建新变量,其值依靠在第一个参数中指定的一个或多个源变量的值。map 块内部的参数指定源值和成果值之间的映射。源值被指定为字符串或正则表达式。正则表达式应该以 “” 符号(用于巨细写灵敏的匹配)或 “*” 符号(用于巨细写不灵敏的匹配)最初。正则表达式能够包括命名或位置捕获,能够在其它指令以及成果变量中运用。假如源值匹配下面描绘的特别参数的称号之一,那么它应该以 “\” 符号最初。成果值能够包括文本、变量,及其组合。也支撑下面的特别参数:

  • default 假如源值与指定的变种都不匹配,那么设置成果值。假如未指定 default,那么默许的成果值将为空字符串。
  • hostnames表明源值能够是带前缀或后缀掩码的主机名:
*.example.com 1;
example.*     1;

下面两条记录:

example.com   1;
*.example.com 1;

能够合并为:

.example.com  1;

应该在值列表的前面指定该参数。

  • include 包括包括值的文件,能够有多个包括。
  • volatile表明该变量不可缓存。

假如源值与指定的多个变种匹配,比方与掩码和正则表达式都匹配,那么将依照如下优先级次序,挑选第一个匹配的变种:

  • 不带掩码的字符串值
  • 带前缀掩码的最长字符串值,比方 “*.example.com”
  • 带后缀掩码的最长字符串值,比方 “mail.*”
  • 第一个匹配的正则表达式(依照在装备文件中呈现的次序)
  • 默许值

ngx_http_geo_module 模块

ngx_http_geo_module 模块创建值依靠客户端 IP 地址的变量。

示例装备


geo $geo {
    default        0;
    127.0.0.1      2;
    192.168.1.0/24 1;
    10.1.0.0/16    1;
    ::1            2;
    2001:0db8::/32 1;
}

指令

geo

描绘指定变量的值与客户端 IP 地址的依靠联系。默许情况下,从 $remote_addr 变量获取地址,可是也能够从其它变量获取,比方:

geo $arg_remote_addr $geo {
    ...;
}

假如变量的值不表明有用的 IP 地址,那么运用地址 “255.255.255.255”。能够运用 CIDR 表明法中的前缀(包括独自的地址)或规模来指定地址。也支撑以下特别参数:

  • delete

删去指定网络。

  • default

假如客户端地址不匹配任何指定的地址,那么给变量设置该值。当以 CIDR 表明法指定地址时,能够运用 “0.0.0.0/0” 和 “::/0” 替代 default。当未指定 default 时,默许值将为空字符串。

  • include

包括包括地址和值的文件。能够有多个包括。

  • proxy

界说受信赖的地址。当恳求来自受信赖地址时,将运用 “X-Forwarded-For” 恳求头字段中的地址。与常规地址不同,受信赖地址是次序检查的。

  • proxy_recursive

启用递归地址查找。假如禁用递归查找,那么将运用 “X-Forwarded-For” 中发送的最后一个地址,替代匹配受信赖地址的原始客户端地址。假如启用递归查找,那么将运用 “X-Forwarded-For” 中发送的最后一个非受信赖地址,替代匹配受信赖地址的原始客户端地址。

  • ranges

表明地址被指定为规模。该参数应该是第一个。为加快基于 geo 的加载,地址应按升序摆放。比方:

geo $country {
    default        ZZ;
    include        conf/geo.conf;
    delete         127.0.0.0/16;
    proxy          192.168.100.0/24;
    proxy          2001:0db8::/32;
    127.0.0.0/24   US;
    127.0.0.1/32   RU;
    10.1.0.0/16    RU;
    192.168.1.0/24 UK;
}

conf/geo.conf 文件或许包括如下行:

10.2.0.0/16    RU;
192.168.2.0/24 RU;

最详细匹配的值被运用。比方,关于地址 127.0.0.1,将挑选值 “RU”,而非 “US”。带规模的示例:

geo $country {
    ranges;
    default                   ZZ;
    127.0.0.0-127.0.0.0       US;
    127.0.0.1-127.0.0.1       RU;
    127.0.0.1-127.0.0.255     US;
    10.1.0.0-10.1.255.255     RU;
    192.168.1.0-192.168.1.255 UK;
}

ngx_http_limit_conn_module 模块

ngx_http_limit_conn_module 模块用于依照界说的键,约束衔接数量,特别是来自单个 IP 地址的衔接数量。

并非所有衔接都被计数。只要具有正在被服务端处理的恳求,而且全部恳求头已被读取的衔接才被计数。

示例装备

http {
    limit_conn_zone $binary_remote_addr zone=addr:10m;

    ...

    server {

        ...

        location /download/ {
            limit_conn addr 1;
        }

指令

limit_conn

设置同享内存区域,以及关于给定的键值,答应的最大衔接数量。当超越该约束时,服务端回复恳求时,将回来过错。比方,指令:

limit_conn_zone $binary_remote_addr zone=addr:10m;
server {
    location /download/ {
        limit_conn addr 1;
    }

每个 IP 地址同一时刻仅答应一个衔接。能够有多个 limit_conn 指令。比方,以下装备将约束每个客户端 IP 到服务端的衔接数,同时约束到虚拟主机的总衔接数:

limit_conn_zone $binary_remote_addr zone=perip:10m;
limit_conn_zone $server_name zone=perserver:10m;
server {
    ...
    limit_conn perip 10;
    limit_conn perserver 100;
}

当且仅当当时层级未界说 limit_conn 指令时,早年面的装备层级承继这些指令。

limit_conn_zone zone=:

设置同享内存区域的参数,它将为多个键保存状况。特别是包括当时衔接数的状况。key 能够包括文本、变量,及其组合。不核算具有空键值的恳求。运用示例:

limit_conn_zone $binary_remote_addr zone=addr:10m;

在这里,客户端 IP 地址用作键。留意,这里运用 binaryremoteaddr变量替代binary_remote_addr 变量替代 remote_addr。remoteaddr变量的巨细能够从7到15个字节不等。存储的状况在32位平台上,占用32或64字节的内存,在64位平台上,一直占用64字节的内存。remote_addr 变量的巨细能够从 7 到 15 个字节不等。存储的状况在 32 位平台上,占用 32 或 64 字节的内存,在 64 位平台上,一直占用 64 字节的内存。binary_remote_addr 变量的巨细关于 IPv4 地址一直是 4 字节,关于 IPv6 地址一直是 16 字节。存储的状况在 32 位平台上,运用占用 32 或 64 字节,在 64 位平台上,占用 64 字节。1M 区域能够保存大约 3.2 万 32 字节状况,或大约 1.6 万 64 字节状况。假如区域存储被耗尽,那么服务端将对后续所有恳求回来过错。

内嵌变量

$limit_conn_status

保存约束衔接数的成果:PASSED、REJECTED 或 REJECTED_DRY_RUN。

ngx_http_limit_req_module 模块

ngx_http_limit_req_module 模块用于依照界说的键,约束恳求处理速率,特别是来自单个 IP 地址的恳求处理速率。该模块运用“漏桶”方法进行约束。

装备示例

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

指令

limit_req zone= [burst=] [nodelay | delay=]

设置同享内存区域,以及恳求的最大突发巨细。假如恳求速率超越为区域装备的速率,那么推迟处理它们,以便以界说的速率处理恳求。过多的恳求将被推迟,直到它们的数量超越最大突发巨细,此时将以过错停止恳求。默许情况下,最大突发巨细等于零。比方,指令:

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

均匀情况下每秒钟最多答应 1 个恳求,突发恳求不超越 5 个。

当恳求正被约束时,假如不期望推迟处理,那么应该运用参数 nodelay:

limit_req zone=one burst=5 nodelay;

delay 参数指定超越约束的恳求被推迟的阈值。默许是 0,即所有超限恳求都被推迟。

能够有多个 limit_req 指令。比方,以下装备将约束来自于单个 IP 地址的恳求处理速率,同时按虚拟主机约束恳求处理速率:

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;
}

当且仅当当时层级中未界说 limit_req 指令时,早年面的装备层级承继这些指令。

limit_req_zone zone=: rate= [sync]

为同享内存区域设置参数,它将为多个键保存状况。特别是存储超限恳求的当时数量的状况。key 能够包括文本、变量,及其组合。不核算具有空键值的恳求。运用示例:

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

在这里,状况被保存在 10M 的区域 “one” 中,该区域的均匀恳求处理速率不能超越每秒 1 个恳求。

客户端 IP 地址用作键。留意,这里运用 binaryremoteaddr替代binary_remote_addr 替代 remote_addr。$binary_remote_addr 变量的巨细关于 IPv4 地址一直是 4 字节,关于 IPv6 地址是 16 字节。存储的状况在 32 位平台上一直占用 64 字节,在 64 位平台上占用 128 字节。1M 的区域能够存储大约 1.6 万 64 字节状况,或大约 8 千 128 字节状况。

假如区域存储被耗尽,那么将删去最近最少运用的状况。在无法创建新状况后,将以过错停止恳求。

用每秒恳求数(r/s)指定速率。假如期望运用每秒少于 1 个恳求的速率,那么用每分钟恳求数(r/m)指定速率。比方,每秒半个恳求上 30r/m。

内嵌变量

$limit_req_status

保存约束恳求处理速率的成果:PASSED、DELAYED、REJECTED、DELAYED_DRY_RUN 或 REJECTED_DRY_RUN。

ngx_http_core_module 模块

指令

limit_rate

约束向客户端传输响应的速率。用每秒字节数指定 rate。0 值禁用速率约束。针对每个恳求设置该约束,因而假如客户端并发地翻开两个衔接,那么总速率将为指定的约束的两倍。参数值能够包括变量。在根据特定条件约束速率的场景下,这或许很有用:

map $slow $rate {
    1     4k;
    2     8k;
}
limit_rate $rate;

也能够在 $limit_rate 变量中设置速率约束。但不建议运用该方法:

server {
    if ($slow) {
        set $limit_rate 4k;
    }
    ...
}

也能够在署理服务响应的 “X-Accel-Limit-Rate” 头字段中设置速率约束。能够运用 proxy_ignore_headers、fastcgi_ignore_headers、uwsgi_ignore_headers 和 scgi_ignore_headers 指令禁用该功用。

limit_rate_after

设置初始数量,之后向客户端持续传输响应将被限速速率。参数值能够包括变量。

示例:

location /flv/ {
    flv;
    limit_rate_after 500k;
    limit_rate       50k;
}

示例

方针:假如客户端 IP 不地址在白名单中,那么依照客户端 IP 地点的网段约束衔接数,以及每个衔接的恳求速率。示例装备:

geo $limit_key {
    default "limit_group_default";
    # 白名单
    127.0.0.0/24 "";
    # 约束组 limit_group_a
    10.0.2.0/24 "limit_group_a";
    10.0.3.0/24 "limit_group_a";
}
limit_conn_zone $limit_key zone=conn_limit_zone:10m;
limit_conn conn_limit_zone 1;
limit_req_zone $limit_key zone=req_limit_zone:10m rate=1r/s;
limit_req zone=req_limit_zone burst=5;