NGINX Full Version

使用 NGINX Plus 键值存储实现动态带宽限制

键值存储(key-value store)特性是在 NGINX Plus Release 13 中针对 HTTP 引入的,并在 Release 14 中扩展到 TCP 和 UDP。通过键值存储,您可以根据各种自定义 NGINX Plus 处理流量的方式,而无需重新加载(即无需 reload)配置或重新启动 NGINX Plus。

编者按 — 在 NGINX Plus R16 及更高版本中,键值存储可以在集群中的所有 NGINX Plus 实例之间同步。(集群中的状态共享也可用于 NGINX Plus 的其他功能。)有关详细信息,请参阅我们的博客和《NGINX Plus 管理指南》。

在之前的博客文章中,我们介绍了如何使用键值存储来构建 IP 地址黑名单和进行 A/B 测试进行 A/B 测试。在这篇博客文章中,我将介绍如何使用键值存储来根据应用性能目标为不同类别的用户设置不同的带宽限制。这种做法通常也是服务等级(class of service)的一种实现

 

限制带宽

在 web 应用中限制带宽的原因有很多。例如,在一个电子商务网站上,您可以通过对游客用户限制带宽,增强那些有意下单的用户的使用体验。这也可用于保护所有合法用户不受恶意用户的伤害,避免影响网站性能。

在我们的示例中,我们为游客设置最低带宽限值,为已登录的用户设置更高的限值,为首选用户设置更高的限制,为已登录且购物车中有商品的用户设置最高的限值。

您创建的按用户类别调整带宽的工具称为“动态带宽限制器”。您可以使用 NGINX Plus 的两个主要功能来实现:

 

配置动态带宽限制器

为了创建动态带宽限制器,我们在 NGINX Plus 配置中设置了一个默认限值,该值适用于大多数的游客(未登录的用户)。对于越来越有价值的用户,带宽限制更为宽松,一个外部应用负责将客户分配到键值存储中的某个服务级别。

在下面的示例中,keyval_zone 指令为名为 ratezone 的键值存储分配 1 MB 的共享内存。在 keyval 指令中,第一个参数将记录在 cookie 中的客户 ID 定义为键值存储中查找的键,第二个参数指定将 $bwlimit 变量设置为与键(该客户的服务级别)关联的值。

$cookie_CUSTOMERID 变量表示名为 CUSTOMERID 的 cookie,这是一种用于唯一标识用户的简单机制,通常在用户登录时由应用设置。它最常用于跟踪用户的站点浏览,从而提供会话持久性。

键值存储依赖于 NGINX Plus API,该 API 通过在 /api 位置添加 api 指令来实现,并通过 write=on 参数实现读写访问。(另一个 location 指令启用内置的 NGINX Plus 实时活动监控仪表盘,它也使用 NGINX Plus API。)

注意:此代码段不限制对 API 的访问,但我们强烈建议您实际使用要对 API 访问加以一定限制,尤其是在生产环境中。有关更多信息,请参阅《NGINX Plus 管理指南》。

keyval_zone zone=ratezone:1M;
keyval $cookie_CUSTOMERID $bwlimit zone=ratezone;

server {
    listen 8080;

    location = /dashboard.html {
        root /usr/share/nginx/html;
    }

    location /api {
        api write=on;
    }
#...
}

然后,我们使用 map 代码块来定义每类服务的带宽限值。根据 keyval 指令的设置,$bwlimit 变量捕捉外部应用在键值存储中设置的客户服务级别:已登录用户使用青铜级,首选用户使用白银级,购物车中有商品的用户使用黄金级

映射块为未登录或键值存储中没有条目的用户分配 256 Kbps 的默认带宽限值。服务级别的限值为青铜级 512 Kbps,白银级 1 Mbps,黄金级黄金级无限带宽(用 0 表示)。您可能还想为购物车用户设置一个限值,这取决于您的应用性能体验。$ratelimit 变量捕捉带宽限值。

server 代码块中的 set 指令将内置的 $limit_raterate 变量设置为映射块中设置的 $ratelimit 值来实现带宽限制。

map $bwlimit $ratelimit {
    bronze 512k;
    silver 1m;
    gold 0;
    default 256k;
}

server {
    #...
    set $limit_rate $ratelimit;
    #... 
}

 

使用 NGINX Plus API 更新键值存储

NGINX Plus API 中的 HTTP GETPOSTPATCH 方法用于访问和管理键值存储。如前所述,在我们的示例中,一个外部应用管理键值存储。为了便于说明,我们将在这里使用 curl 命令来显示可能的操作。

首先,我们使用 GET 方法验证 ratezone 键值存储是否为空。第二个 curl 命令请求文件 brownie.jpg 文件,使用 -b 选项将客户 ID 作为 HTTP Cookie 标头的值。-o 选项将文件写入 /dev/null(有效丢弃),因为这里的重点是查看下载速率,而不是查看 brownie.jpg

因为键值存储是空的,所以客户没有键,我们预计他将获得 256 Kbps 的默认速率限值。Current Speed 列中报告的 276 Kbps 速率证实了这一点。

root# curl -X GET localhost:8080/api/1/http/keyvals/ratezone
{}

root# curl -b "CUSTOMERID=CPE1704TKS" -o /dev/null localhost/brownie.jpg
  % Total    % Received % Xferd  Average Speed   Time    Time     Time     Current
                                 Dload  Upload   Total   Spent    Left     Speed
100 1893k  100    1893k 0     0   269k       0  0:00:07 0:00:07 --:--:--     276k

现在我们使用 HTTP POST 方法在键值存储中创建一个条目,该条目将客户 CPE1704TKS 分配到青铜色服务级别。第二个命令确认新条目的存在,最后一个命令显示下载速度增加到 626 Kbps,反映了青铜色青铜色服务级别的更高限值。

root# curl -X POST -d '{"CPE1704TKS":"bronze"}' localhost:8080/api/1/http/keyvals/ratezone

root# curl -X GET localhost:8080/api/1/http/keyvals/ratezone
{"CPE1704TKS":"bronze"}

root# curl -b "CUSTOMERID=CPE1704TKS" -o /dev/null localhost/brownie.jpg
  % Total    % Received % Xferd  Average Speed   Time    Time     Time     Current
                                 Dload  Upload   Total   Spent    Left     Speed
100 1893k  100    1893k 0     0     626k     0  0:00:03 0:00:03  --:--:--     626k

然后我们使用 PATCH 方法将客户提升到银色服务级别,并再次确认下载速率的提升。

root# curl -X PATCH -d '{"CPE1704TKS":"silver"}' localhost:8080/api/1/http/keyvals/ratezone

root# curl -X GET localhost:8080/api/1/http/keyvals/ratezone
{"CPE1704TKS":"silver"}

root# curl -b "CUSTOMERID=CPE1704TKS" -o /dev/null localhost/brownie.jpg
  % Total    % Received % Xferd  Average Speed   Time    Time     Time     Current
                                 Dload  Upload   Total   Spent    Left     Speed
100 1893k  100    1893k 0     0   1876k      0  0:00:01 0:00:01  --:--:--    1877k

最后,我们再次使用 PATCH 方法将客户提升到金色服务级别,并确认在没有强加带宽限制的情况下,下载速率大幅提高。

root# curl -X PATCH -d '{"CPE1704TKS":"gold"}' localhost:8080/api/1/http/keyvals/ratezone

root# curl -X GET localhost:8080/api/1/http/keyvals/ratezone
{"CPE1704TKS":"gold"}

root# curl -b "CUSTOMERID=CPE1704TKS" -o /dev/null localhost/brownie.jpg
  % Total    % Received % Xferd  Average Speed   Time    Time     Time     Current
                                 Dload  Upload   Total   Spent    Left     Speed
100 1893k  100    1893k 0     0    192M      0 --:--:-- --:--:-- --:--:--     205M

 

此示例演示了如何使用客户 ID cookie ($cookie_CUSTOMERID 来标识每个用户并分配服务级别。然而,这种逻辑几乎可以应用到 NGINX 变量中捕捉的任何值($remote_ip$jwt_claim_sub等)。根据应用或用例的不同,有些变量比其他变量更有意义。您还可以使用键值存储来限制 connection rate 并发连接,这对于防止临时用户或恶意用户对其他用户(尤其是活跃的购买者)和应用的总体性能产生负面影响非常有用。

 

完整的 NGINX 配置示例

keyval_zone zone=ratezone:1M;
keyval $cookie_CUSTOMERID $bwlimit zone=ratezone;

map $bwlimit $ratelimit {
    bronze 512k;
    silver 1m;
    gold 0;
    default 256k;
}

upstream backend1 {
    zone backend1 64k;
    server 192.168.1.100:443;
}

server {
    listen 8080;

    location = /dashboard.html {
        root /usr/share/nginx/html;
    }

    location /api {
        api write=on;
    }

    location / {
        return 301 /dashboard.html;
    }
}

server {
    listen 80;
    set $limit_rate $ratelimit;

    location /favicon.ico {
        alias /usr/share/nginx/html/nginx-favicon.png;
    }
	
    location = /brownie.jpg {
        root /usr/share/nginx/html;
    }

    location / {
        proxy_set_header Host $host;
        proxy_pass https://backend1;
    }	
}

如欲试用 NGINX Plus 键值存储,请立即下载 30 天免费试用版,或与我们联系以讨论您的用例。