背景
我有以下 Nginx 配置,它对在 2 个不同的服务器上运行的 2 个 Tomcat 服务器进行负载平衡:
### http://nginx.org/en/docs/http/ngx_http_upstream_module.html#hash
upstream backend_w_affinity {
hash $request_uri consistent;
server 192.168.110.11:8080;
server 192.168.110.12:8080;
}
server {
listen 80 default_server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
location ~ ^/path/to/rest/endpoint$ { proxy_pass http://backend_w_affinity; }
}
当我使用以下curl
命令在 Nginx 上驱动流量时,一切正常。
$ watch "curl -iI http://10.128.13.153/path/to/rest/endpoint"
HTTP/1.1 401 Unauthorized
Server: nginx/1.8.0
Date: Tue, 09 Jun 2015 01:31:13 GMT
Content-Type: application/xml;charset=ISO-8859-1
Content-Length: 231
Connection: keep-alive
笔记:我意识到我上面得到了 401 错误。肯定是 Tomcat 启动了,如果我改变路径,让它指向 Tomcat 中的静态页面,这样我就能得到 200 错误,它仍然会以相同的方式运行,所以目前这似乎不是问题。
乍一看还不错
运行上述curl
命令后,我的所有流量都会定向到第一个 Tomcat 服务器。如果我停止 Tomcat,则所有流量都会故障转移到第二个 Tomcat 服务器。
我的问题
当我重新启动第一台 Tomcat 服务器时,我期望所有流量现在都保留在第二台服务器上。然而,一旦我重新启动它,流量就会被定向回第一台 Tomcat 服务器。
我怎样才能让 Nginx 将流量留在第二台服务器上,而不是将其重定向回来?
答案1
如果您希望流量即使在上游组成员发生变化的情况下也能粘在服务器上,则需要使用“粘性”会话。只要服务器可用,一致性哈希就会选择同一台服务器,这似乎不是您想要的。
在您的例子中,您正在对 URI 进行哈希处理,如果您在 nginx 中使用 proxy_cache,这有利于“缓存局部性”。但是,一致性哈希处理意味着一台服务器将始终是给定 URI 的“首选”,因此一旦该服务器重新上线,nginx 就会切换回该服务器。
有第三方开源 nginx 提供粘性会话模块或有本地人商业“NGINX Plus”中的粘性会话选项。
无需附加模块或商业 nginx 订阅即可实现粘性会话,但这样做有点丑陋。您可以使用 map、proxy_pass 与目标中的可变主机名以及回退位置的组合。您还需要在每个后端服务器中设置一个唯一的标头来标识它们(因此它们发送了一个“X-Loadbalace-ID”标头。这可能是会话 cookie 的一部分,例如 Tomcat JVM 路由。我们实际上仍在生产中运行此配置,因为它早于 nginx 上粘性会话的可用性。
类似这样的方法可能对你有用(我们所做的未经测试的简化):
upstream backend_w_affinity {
#use round-robin
server 192.168.110.11:8080;
server 192.168.110.12:8080;
}
server {
listen 80 default_server;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
map $http_cookie_lb $mybackend {
server1 192.168.110.11:8080;
server2 192.168.110.12:8080;
#default is something that will fail quickly
#when no correct load balancing cookie is set
#putting us into the named location that uses
#the upstream block
default 127.0.0.1:65432;
}
location ~ ^/path/to/rest/endpoint$ {
proxy_pass http://$mybackend;
#if the backend selected is down, fallback to upstream logic
error_page 503 = @backend_down;
}
location @backend_down {
proxy_pass http://backend_w_affinity;
add_header "Set-Cookie" "lb=$upstream_http_X_Loadbalance_ID";
}
}