写点什么

《NGINX 完全指南》章节精选 | 流量管理和可编程性

  • 2023-12-06
    北京
  • 本文字数:4051 字

    阅读完需:约 13 分钟

原文作者:Derek DeJonghe

原文链接:《NGINX 完全指南》章节精选 | 流量管理和可编程性

转载来源:NGINX 开源社区


NGINX 唯一中文官方社区 ,尽在 nginx.org.cn



编者按——O'Reilly 出版的《NGINX 完全指南》中文版(2022 年最新版)现已正式上线,现在点击即可免费下载电子版全本。


这本 180 多页易于理解的配置指南将为您讲述从如何安装 NGINX 到如何进行配置,再到如何调试和排除应用性能故障。不仅如此,本书还提供了将 NGINX 用于负载均衡、云部署、自动化、容器和微服务、服务网格、安全防护等场景的相关配置示例。 


本文中,本书的中文校译熊平为大家节选了本书的第三章《流量管理》和第五章《可编程性和自动化》中的精彩段落:A/B 测试、限制速率、使用通用编程语言扩展 NGINX。


A/B 测试


问题


在文件或应用的两个或多个版本之间分割客户端流量,以测试接受度或参与度。


解决方案


使用 split_clients 模块将一定比例的客户端流量定向到一个不同的上游 (upstream) 池:


split_clients "${remote_addr}AAA" $variant {       20.0% "backendv2";       *     "backendv1";}
复制代码


split_clients 指令对作为第一个参数提供的字符串进行哈希处理,并用该哈希值除以提供的百分比,以映射作为第二个参数提供的变量的值。在第一个参数中添加“AAA”是为了证明这是一个可以包含多个变量的串联字符串,如通用哈希负载均衡算法中所述。


第三个参数是一个包含键值 (key-value) 对的对象,其中键是百分比权重,值是要分配的值。键可以是百分比或星号。星号表示取完所有百分比后的剩余部分。对于 $variant 变量的值,backendv2 为客户端 IP 地址流量的 20% ,

backendv1 为其余 80%。


在此示例中,backendv1 和 backendv2 表示上游服务器池,可以与 proxy_pass 指令一起使用,如下所示:


location / {       proxy_pass http://$variant}
复制代码


使用变量 $variant 时,我们的流量将会分配给两个不同的应用服务器池。


为了理解 split_clients 的广泛用途,我们通过一个例子来看看两个静态站点版本之间的流量分割:


http {              split_clients "${remote_addr}" $site_root_folder {                            33.3% "/var/www/sitev2/";                            * "/var/www/sitev1/";              }              server {                            listen 80 _;                            root $site_root_folder; location / {                                          index index.html;                            }       }}
复制代码


详解


在测试电子商务网站上不同类型的营销和前端功能的转化率时,这种类型的 A/B 测试非常有用。应用通常通过“灰度发布”进行部署。在这种类型的部署中,用户可以逐步增加路由到新版本的用户百分比,进而将流量慢慢切换到新版本。在推出新版本的代码时,在不同的应用版本之间分割客户端流量非常实用,这可以降低发生错误后带来的影响。


更常见的部署模式是蓝绿部署,也就说在将用户切换到新版本的同时,仍然保持旧版本在验证部署期间的可用性。无论出于何种原因在两组不同的应用之间分割客户端流量,NGINX 的 split_client 模块都可以帮您轻松搞定。


限制速率


问题


通过预定义的键(例如客户端的 IP 地址)来限制请求的速率。


解决方案


利用限速模块限制请求速率:


http {             limit_req_zone $binary_remote_addr zone=limitbyaddr:10m rate=3r/s;             limit_req_status 429;             # ...             server {                           # ...                           limit_req zone=limitbyaddr;                           # ...             }}
复制代码


此示例配置创建了一个名为 limitbyaddr 的共享内存区。使用的预定义键是二进制形式的客户端 IP 地址。共享内存区的大小设置为 10 MB。该区域使用关键字参数设置速率。limit_req 指令使用了一个必不可少的关键字参数:zone。


zone 指示了要使用哪个共享内存请求限制区的指令。根据 limit_req_status 指令的定义,超过明示速率的请求将返回 429 HTTP 代码。建议设置一个 400 级范围的状态码,因为默认值是 503,这代表服务器有问题,而实际问题是出在客户端方面。


使用 limit_req 指令的可选关键字参数来启用两级速率限制:


server {              location / {                            limit_req zone=limitbyaddr burst=12 delay=9;              }}
复制代码


在某些情况下,客户端需要同时发出许多请求,此后先在一段时间内降低速率,然后再发出更多请求。您可以使用关键字参数 burst 允许客户端超过其速率限制但不拒绝其请求。超出速率的请求将延迟处理,以将速率限制匹配到配置的值。


有一组关键字参数可以改变这种行为,即 delay 和 nodelay。nodelay 参数不带值,只允许客户端一次性消耗所有流量突发值;但是必须要先等待足够的时间,直到满足速率限制要求为止,否则所有请求都会被拒绝。


在此示例中,如果我们使用 nodelay,客户端可以在第一秒消耗 12 个请求,但是必须要在初始请求之后等待 4 秒才能发出另一个请求。delay 关键字参数定义了在不限流的情况下可以预先发出多少请求。在这种情况下,客户端可以毫无延迟地预先发出 9 个请求,接下来的 3 个将受到限制,此后 4 秒内的任何请求都将被拒绝。


详解


限速模块非常强大,可以防止滥用快速请求,同时仍然为每个人提供优质服务。限制请求速率的原因有很多,安全性就是其中之一。您可以通过严格限制登录页面的速率来防御暴力破解攻击。您可以对所有请求设置合理的限制,从而防止恶意用户试图对您的应用拒绝服务或浪费资源的不轨行为。


限速模块的配置很像连接限制模块,并且存在许多相同的问题。您可以按照每秒限速,也可以按照每分钟限速。当达到速率限制时,日志就会记录事件。


此外,还有一条指令没有在示例中给出:limit_req_log_level,它的默认值为 error,您也可以把它设置为 info、

notice 或 warn。在 NGINX Plus 中,速率限制目前具有集群感知能力。


测试限制可能是个很棘手的问题,测试方案通常很难在替代环境中模拟实时流量。在这种情况下,您可以将

limit_req_dry_run 指令设置为 on,然后使用访问日志中的变量 $limit_req_status。$limit_req_status 变量将计算为 PASSED、REJECTED 或 REJECTED_DRY_RUN。


启用 dry run 后,您将能够分析实时流量日志,并在真正实施限制前根据需要调整限制,从而确保您的限制配置正确。


使用通用编程语言扩展 NGINX


问题


您需要 NGINX 使用通用编程语言执行一些自定义扩展。


解决方案


在用 C 语言编写自定义 NGINX 模块之前,请先评估您的用例是否适合使用其他编程语言模块。C 编程语言是一种非常强大且高效的语言,但是也有许多其他语言模块能够支持所需的自定义。


NGINX 引入了 NGINX JavaScript (NJS),只需启用一个模块就可以将 JavaScript 的强大功能暴露到 NGINX 配置中。您也可以使用 Lua 和 Perl 模块。


使用这些语言模块时,您要么需要导入一个包含代码的文件,要么直接在配置中定义代码块。


要使用 Lua,请安装 Lua 模块和以下 NGINX 配置,以定义一个内联的 Lua 脚本:


load_module modules/ndk_http_module.so;load_module modules/ngx_http_lua_module.so;events {}http {       server {                     listen 8080;                     location / {                                   default_type text/html;                                   content_by_lua_block {                                                 ngx.say("hello, world")                                   }                     }               }}
复制代码


Lua 模块通过 ngx 模块定义的对象提供自己的 NGINX API。与 NJS 中的 request 对象一样,ngx 对象也有描述请求和操作响应的属性和方法。


安装了 Perl 模块后,此示例将使用 Perl 从运行时环境设置 NGINX 变量:


load_module modules/ngx_http_perl_module.so;events {}       http {                   perl_set $app_endpoint 'sub { return $ENV{"APP_DNS_ENDPOINT"}; }';                   server {                                 listen 8080;                                 location / {                                               proxy_pass http://$app_endpoint                                 }                   }               }}
复制代码


上述示例表明,这些语言模块不仅仅是返回响应,而且还会暴露更多功能。perl_set 指令将一个 NGINX 变量设置为从 Perl 脚本返回的数据。这个示例比较有限,只是返回了用作请求代理端点的系统环境变量。


详解


NGINX 具备出色的可扩展性,这为启用更多功能开创了更多可能。NGINX 可通过使用 C 语言模块自定义代码来扩展,您可以从一开始构建时就把这些模块编译到 NGINX 中,也可以在配置中动态加载。


目前还有一些模块可以暴露 JavaScript (njs)、Lua 和 Perl 的功能与语法。在多数情况下这些预先存在的模块是够用的 —— 除非将 NGINX 的自定义功能分配出去。现在开源社区中有很多为这些模块构建的脚本。


该解决方案演示了 NGINX 和 NGINX Plus 中可用的 Lua 和 Perl 脚本语言的基本用法。无论是寻求响应、设置变量、发送子请求还是定义复杂的重写,这些 NGINX 模块都能提供相应的支持。


本节中文版校译

熊平

F5 NGINX 资深架构师


拥有超过 20 年应用交付领域工作经验。拥有 F5 认证方案专家、NGINX 认证教练、CKA、亚马逊云科技架构师以及 TOGAF 等多项行业认证,对微服务架构、应用高可用、应用优化和安全防护有着独特的见解,以及深厚的积累。


NGINX 唯一中文官方社区 ,尽在 nginx.org.cn

更多 NGINX 相关的技术干货、互动问答、系列课程、活动资源: 开源社区官网 | 微信公众号

用户头像

NGINX唯一中文官方社区 2022-07-04 加入

- 微信公众号:https://mp.weixin.qq.com/s/XVE5yvDbmJtpV2alsIFwJg - 微信群:https://www.nginx.org.cn/static/pc/images/homePage/QR-code.png?v=1621313354 - B站:https://space.bilibili.com/628384319

评论

发布
暂无评论
《NGINX 完全指南》章节精选 | 流量管理和可编程性_A/B 测试_NGINX开源社区_InfoQ写作社区