键值存储(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 和 NGINX Plus 的核心特性:限制响应速度的能力。使用
limit_rate
指令或$limit_rate
变量指定最大吞吐量。该变量的优势在于您可以为不同的条件设置不同的速率,就像我们在示例中所做的那样。(编者注:$limit_rate 的变量方式指定在 1.17.0 之后不是推荐方式,详情请查看 http://nginx.org/en/docs/http/ngx_http_core_module.html#limit_rate) - 第二个功能是较新的功能:NGINX Plus 中的键值存储。NGINX Plus 键值存储可以包含 JSON 格式的密钥和值;它存储在内存中,以便快速访问。
配置动态带宽限制器
为了创建动态带宽限制器,我们在 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_rate
rate 变量设置为映射块中设置的 $ratelimit
值来实现带宽限制。
map $bwlimit $ratelimit {
bronze 512k;
silver 1m;
gold 0;
default 256k;
}
server {
#...
set $limit_rate $ratelimit;
#...
}
使用 NGINX Plus API 更新键值存储
NGINX Plus API 中的 HTTP GET
、POST
和 PATCH
方法用于访问和管理键值存储。如前所述,在我们的示例中,一个外部应用管理键值存储。为了便于说明,我们将在这里使用 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;
}
}