在《使用 NGINX 和 NGINX Plus 实现负载均衡(第 1 部分)》中,我们设置了一个简单的 HTTP 代理来跨多台 Web 服务器对流量进行负载均衡。本文将介绍 NGINX Plus 具备的一些其他功能:通过 keepalives、健康检查、会话保持、重定向及内容重写来优化性能。
有关 NGINX 和 NGINX Plus 负载均衡功能的详细信息,请参阅《NGINX Plus 管理指南》。
编者按 — NGINX Plus Release 5 及更高版本还可以对基于 TCP 的应用进行负载均衡。Release 6 通过增添健康检查、动态重新配置、SSL 终止等功能,显著扩展了 TCP 负载均衡。在 NGINX Plus Release 7 及更高版本中,TCP 负载均衡器具备与 HTTP 负载均衡器一样的功能。Release 9 中引入了对 UDP 负载均衡的支持。
您可以在 stream
上下文(而非 http
上下文)中配置 TCP 和 UDP 负载均衡。由于 HTTP 和 TCP/UDP 之间的固有差异,可用指令和参数略有不同;详情请参阅 HTTP 和 TCP/UDP 上游模块文档。
快速回顾
回顾一下,这是我们在上一篇文章中创建的配置:
server {
listen 80;
location / {
proxy_pass http://backend;
# 将“Host”请求头重写为客户端请求中的值
# 或主服务器名称
proxy_set_header Host $host;
# 或者,将值写入配置:
# proxy_set_header Host www.example.com;
}
}
upstream backend {
zone backend 64k; # 使用 NGINX Plus 的共享内存
least_conn;
server webserver1 weight=1;
server webserver2 weight=4;
}
本文将介绍一些配置 NGINX 和 NGINX Plus 的简单方法,以提高负载均衡效率。
HTTP keepalive
在 NGINX 或 NGINX Plus 与上游服务器之间启用 HTTP keepalive 可提高性能(通过减少延迟),并降低 NGINX 耗尽临时端口的可能性。
HTTP 协议使用底层 TCP 连接来传输 HTTP 请求并接收 HTTP 响应。HTTP keepalive 连接允许重用这些 TCP 连接,从而避免了为每个请求创建和终止连接的开销:
NGINX 是一个全代理,可独立管理客户端连接(前端 keepalive 连接)和服务器连接(上游 keepalive 连接):
NGINX 会维护 keepalive 连接的“缓存”(一组与上游服务器的空闲 keepalive 连接),当需要将请求转发到上游服务器时,它会使用这些缓存中已建立的 keepalive 连接,而非创建新的 TCP 连接。这可减少 NGINX 与上游服务器之间的事务延迟,并降低临时端口的使用率,因此 NGINX 能够处理大量流量并对其进行负载均衡。当流量激增时,这些缓存会被清空,在这种情况下,NGINX 将与上游服务器建立新的 HTTP 连接。
对于其他负载均衡工具,该技术有时称为“多路复用”、“连接池”、“连接复用”或“OneConnect”。
您可通过将 proxy_http_version
、proxy_set_header
及 keepalive
指令添加到配置中来配置 keepalive 连接缓存:
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
}
}
upstream backend {
server webserver1;
server webserver2;
# 与上游服务器组保持多达 20 个空闲连接
keepalive 20;
}
健康检查
启用健康检查不仅能够提高负载均衡服务的可靠性,降低最终用户出错率,而且还便于执行常见维护操作。
NGINX Plus 的健康检查功能可用于检测上游服务器的故障。NGINX Plus 使用“综合事务”探测每台服务器,并根据您在 health_check
指令中配置的参数(以及添加 match
参数的情况下,关联的 match
配置块)检查响应:
server {
listen 80;
location / {
proxy_pass http://backend;
health_check interval=2s fails=1 passes=5 uri=/test.php match=statusok;
# 健康检查继承其他代理设置
proxy_set_header Host www.foo.com;
}
}
match statusok {
# 用于 /test.php 健康检查
status 200;
header Content-Type = text/html;
body ~ "Server[0-9]+ is alive";
}
健康检查从其父 location
块继承一些参数。如果在配置中使用运行时变量,这可能会导致出现问题。例如,以下配置适用于实际 HTTP 流量,因为它从客户端请求中提取 Host
请求头的值。但该配置不适用于健康检查所用的综合事务,因为未对这些事务设置 Host
请求头,这意味着综合事务中没有使用 Host
请求头。
location / {
proxy_pass http://backend;
# 该健康检查可能不起作用……
health_check interval=2s fails=1 passes=5 uri=/test.php match=statusok;
# 从请求中提取“Host”请求头
proxy_set_header Host $host;
}
一种好办法是创建一个虚拟 location 块,以静态定义健康检查事务使用的所有参数:
location /internal-health-check1 {
internal; # 防止外部请求匹配该 location 块
proxy_pass http://backend;
health_check interval=2s fails=1 passes=5 uri=/test.php match=statusok;
# 明确设置请求参数;不使用运行时变量
proxy_set_header Host www.example.com;
}
更多信息,请参阅《NGINX Plus 管理指南》。
会话保持
借助会话保持,可以让无法部署至集群的应用也能实现负载均衡和可靠扩展。存储和复制会话状态的应用可以更高效地运行,帮助提升最终用户性能。
某些应用有时会将状态信息存储到上游服务器,例如当用户将商品放入虚拟购物车或编辑上传的图片时。在这些情况下,您可能希望将来自该用户的所有后续请求都定向到同一服务器。
会话保持指定了请求所须路由到的目标位置,而负载均衡则允许 NGINX 自由选择最佳上游服务器。借助 NGINX Plus 的会话保持功能,这两个进程可以共存:
如果请求符合会话保持规则
那么使用目标上游服务器
否则应用负载均衡算法选择上游服务器
如果会话保持决策因目标服务器不可用而失败,那么 NGINX Plus 会做出负载均衡决策。
最简单的会话保持方法是“sticky cookie”方法,其中 NGINX Plus 在第一个响应中插入一个 cookie,用于标识 sticky 上游服务器:
sticky cookie srv_id expires=1h domain=.example.com path=/;
在另一种“sticky 路由”方法中,NGINX 会根据 JSESSIONID cookie 等请求参数选择上游服务器:
upstream backend {
server backend1.example.com route=a;
server backend2.example.com route=b;
# 选择第一个非空变量;它应包含“a”或“b”
sticky route $route_cookie $route_uri;
}
更多信息,请参阅《NGINX Plus 管理指南》。
重写 HTTP 重定向
如果某些重定向被破坏,则您需要重写 HTTP 重定向,特别是当您从代理被重定向到真正的上游服务器时。
当您代理到上游服务器时,服务器在本地地址上发布应用,但您通过另一个地址(代理的地址)访问应用。这些地址通常解析为域名,如果服务器和代理的域名不同,就会出现问题。
例如,在测试环境中,您可能直接(通过 IP 地址)或按 localhost 对代理进行寻址。但上游服务器可能会监听真实域名(例如 www.nginx.com)。当上游服务器发出重定向消息(使用 3xx
状态码和 Location
请求头,或者使用 Refresh
请求头)时,消息中可能包含服务器的真实域名。
NGINX 会尝试拦截并纠正这种最常见的问题。如果您需要全权控制以执行特定重写,请使用 proxy_redirect
指令,如下所示:
proxy_redirect http://staging.mysite.com/ http://$host/;
重写 HTTP 响应
有时,您需要重写 HTTP 响应中的内容。也许,如上例所示,响应中包含指向代理以外其他服务器的绝对链接。
您可以使用 sub_filter
指令来定义要应用的重写:
sub_filter /blog/ /blog-staging/;
sub_filter_once off;
一个很常见的问题是 HTTP 压缩的使用。如果客户端发出信号表示可以接受压缩数据,而服务器随后压缩了响应,那么 NGINX 就无法检查和修改响应。最简单的方法是将 Accept-Encoding
请求头设置为空字符串(””),从而将其从客户端请求中删除:
proxy_set_header Accept-Encoding "";
完整示例
下面是一个负载均衡配置模板,它使用了本文提到的所有方法。NGINX Plus 具有的高级功能以橙色字体显示。
[编者按 — 以下配置已更新为使用 NGINX Plus API 对上游组进行实时活动监控和动态配置,取代了最初使用的单独模块。]
server {
listen 80;
location / {
proxy_pass http://backend;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Accept-Encoding "";
proxy_redirect http://staging.example.com/ http://$host/;
# 将 Host 请求头重写为客户端请求中的值
proxy_set_header Host $host;
# 替换对 staging.example.com 的任何内联引用
sub_filter http://staging.example.com/ /;
sub_filter_once off;
}
location /internal-health-check1 {
internal; # 防止外部请求匹配该 location 块
proxy_pass http://backend;
health_check interval=2s fails=1 passes=5 uri=/test.php match=statusok;
# 明确设置请求参数;不使用运行时变量
proxy_set_header Host www.example.com;
}
upstream backend {
zone backend 64k; # 使用 NGINX Plus 的共享内存
least_conn;
keepalive 20;
# 为该上游组应用会话保持
sticky cookie srv_id expires=1h domain=.example.com path=/servlet;
server webserver1 weight=1;
server webserver2 weight=4;
}
match statusok {
# 用于 /test.php 健康检查
status 200;
header Content-Type = text/html;
body ~ "Server[0-9]+ is alive";
}
server {
listen 8080;
root /usr/share/nginx/html;
location = /api {
api write=on; # 实时活动监控和
# 上游组的动态配置
allow 127.0.0.1; # 允许从 localhost 访问
deny all; # 拒绝其他任何位置访问
}
}
如欲试用 NGINX Plus 中所有出色的负载均衡功能,请立即下载 30 天免费试用版或与我们联系以讨论您的用例。