我使用nginx代理并为我保持远程服务器的持久连接。
我已经配置了大约15个与此示例类似的块:
upstream rinu-test {
server test.rinu.test:443;
keepalive 20;
}
server {
listen 80;
server_name test.rinu.test;
location / {
proxy_pass https://rinu-test;
proxy_http_version 1.1;
proxy_set_header Connection "";
proxy_set_header Host $http_host;
}
}
问题是如果无法在一个或多个upstream
块中解析主机名,则nginx将不会(重新)启动。我也不能使用静态IP,其中一些主机明确表示不会这样做,因为IP会发生变化。我看到这个错误消息的所有其他解决方案说要摆脱upstream
并在location
块中做所有事情。这不可能在这里,因为keepalive
只能在upstream
下使用。
我可以暂时承受失去一台服务器但不是全部15台。
编辑:结果nginx不适合这个用例。应使用备用后端(上游)keepalive代理。自定义Node.js替代方案在my answer中。到目前为止,我还没有找到其他任何实际可行的替代方案。
早期版本的nginx(1.1.4之前版本)已经为全球访问量最大的网站提供了大量数据(有些人甚至现在都做,如果要相信服务器标题),甚至不支持keepalive
上的upstream
因为在数据中心设置中这样做的好处很少,除非你的各个主机之间有一个非平凡的延迟;请参阅https://serverfault.com/a/883019/110020以获得一些解释。
基本上,除非你知道你特别需要在你的上游和前端之间保持连接,否则它只会使你的架构不那么有弹性而且更糟糕。
(请注意,您当前的解决方案也是错误的,因为IP地址的更改同样不会被检测到,因为您只在配置重新加载时进行主机名解析;因此,即使nginx启动,它也会在IP地址后停止工作上游服务器确实发生了变化。)
选择一个潜在的解决方案:
upstream
keepalive
在数据中心环境中可能是不必要的,并使用proxy_pass
变量为每个请求提供最新的DNS解析(nginx仍然足够智能 - 足以继续缓存分辨率)resolve
上下文中具有server
指令的upstream
参数。set
variable和/或map
来指定upstream
中的服务器;这既未得到证实也未被否认已经实施;例如,它可能或可能不起作用。一种可能的解决方案是涉及本地DNS缓存。它可以是本地DNS服务器,如Bind或Dnsmasq(有一些狡猾的配置,请注意nginx也可以使用specified dns server代替系统默认),或者只是在hosts
文件中维护缓存。
似乎使用hosts
文件和一些脚本是非常简单的方法。 hosts文件应该插入静态和动态部分(即cat hosts.static hosts.dynamic > hosts
),动态部分应该由脚本自动生成(和更新)。
也许有意义的是不时检查更改IP的主机名,并更新主机文件并在更改时重新加载nginx中的配置。如果某些主机名无法解析,则应使用旧IP或某些默认IP(如127.0.1.9)。
如果您不需要nginx配置文件中的主机名(即IP足够),可以通过脚本生成带有IP(已解析主机名)的upstream
部分,并将included生成到nginx配置中 - 无需触摸hosts文件在这种情况下。
另一种方法是编写一个只能满足我想要的新服务。以下内容使用Node.js替换代理https连接的nginx
const http = require('http');
const https = require('https');
const httpsKeepAliveAgent = new https.Agent({ keepAlive: true });
http.createServer(onRequest).listen(3000);
function onRequest(client_req, client_res) {
https.pipe(
protocol.request({
host: client_req.headers.host,
port: 443,
path: client_req.url,
method: client_req.method,
headers: client_req.headers,
agent: httpsKeepAliveAgent
}, (res) => {
res.pipe(client_res);
}).on('error', (e) => {
client_res.end();
})
);
}
用法示例:curl http://localhost:3000/request_uri -H "Host: test.rinu.test"
相当于:curl https://test.rinu.test/request_uri
我把resolve参数放在服务器上,你需要在nginx.conf中设置Nginx Resolver,如下所示:
/etc/Nginx/Nginx.conf:
http {
resolver 192.168.0.2 ipv6=off valid=40s; # The DNS IP server
}
site.conf:
upstream rinu-test {
server test.rinu.test:443;
keepalive 20;
}
你的场景非常类似于使用aws ELB
作为uptreams的场景,其中resolve
对于定义域的正确IP至关重要。
您需要做的第一件事是确保您使用的DNS服务器可以解析到您的域,然后您可以像这样创建您的配置:
resolver 10.0.0.2 valid=300s;
resolver_timeout 10s;
location /foo {
set $foo_backend_servers foo_backends.example.com;
proxy_pass http://$foo_backend_servers;
}
location /bar {
set $bar_backend_servers bar_backends.example.com;
proxy_pass http://$bar_backend_servers;
}
请注意resolver 10.0.0.2
它应该是DNS服务器的IP,它可以工作并回答您的查询,具体取决于您的设置,这可能是像unbound这样的本地缓存服务。然后只需使用resolve 127.0.0.1
现在,从文档中使用变量来指定域名非常重要:
当您使用变量在proxy_pass指令中指定域名时,NGINX会在其TTL到期时重新解析域名。
您可以使用dig
等工具检查您的解析器:
$ dig +short stackoverflow.com
如果是必须在上游使用keepalive
,如果不是使用Nginx +的选项,那么你可以尝试openresty balancer,你将需要使用/实现lua-resty-dns