我有一台 Nginx 服务器,它不断处理大量请求。我希望能够更改服务器的配置文件,并在零停机时间内动态重新加载它。
我在 shell 中运行以下行:
httperf --server=127.0.0.1 --port=80 --uri=/ --num-conns=1 --num-calls=10
当它发送请求时,我正在重新加载我的 nginx 配置。我尝试了以下两个选项:
sudo nginx -s reload
sudo kill -s HUP [pid]
这两个请求都会导致 httperf 返回一些错误。平均而言,在两次正常请求之后,httperf 会退出并打印其日志,其中包含以下相关行:
Errors: total 1 client-timo 0 socket-timo 0 connrefused 0 connreset 1
我在很多地方都看到过,重新加载应该无缝完成且没有停机时间,但从我运行的测试来看,情况似乎有所不同。
我的问题是:
- 我是否以错误的方式执行了测试?为什么此连接被重置?
- 这个问题有解决办法吗?
- 我实际上需要一个负载平衡器,可以动态地添加和删除服务器,有没有更好的解决方案可以解决我的问题?
提前感谢您的帮助,期待看到一些有见地的答案。
答案1
要理解以下答案,需要一些背景知识:
我是否以错误的方式进行测试?
是的,你的测试有些不正确。问题是你的测试使用 PERSISTENT 连接发送 10 个请求。你可以通过运行以下测试轻松检查它,并且不会有任何连接重置(因为你每个连接只发送一个请求):
httperf --server=127.0.0.1 --port=80 --uri=/ --num-conns=10 --num-calls=1
为什么我会重置此连接?
如果你看看nginx 文档,你会发现:
旧工作进程收到关闭命令后,将停止接受新连接并继续处理当前请求,直到处理完所有请求。 此后,旧工作进程退出。
这是真的,但文档没有提到持久连接发生了什么。我在旧的邮件列表. 在当前正在运行的请求得到处理后,nginx 将通过[FIN, ACK]
向客户端发送来启动持久连接关闭。
为了检查这一点,我使用了 WireShark 并配置了一个简单的工作程序,该工作程序在收到请求后会休眠 5 秒然后回复。我使用以下命令发送请求:
httperf --server=127.0.0.1 --port=80 --uri=/ --num-conns=1 --num-calls=2
在发出上述命令后,我重新加载了 nginx(当时它正在处理第一个请求)。以下是 WireShark 嗅探到的包:
- 3892-3894-通常建立 TCP 连接。
- 3895 – 客户端发送了第一个请求。
- 3896 – 服务器确认 3895。
- 在这里
nginx reload
被处决。 - 4089 – 服务器已发送响应。
- 4090-服务器发送关闭连接信号。
- 4091-客户端确认 4089。
- 4092-客户端确认 4090。
- 4093 - 客户端发送了第二个请求(什么鬼?)
- 4094-客户端发送关闭连接信号。
- 4095-服务器确认4093。
- 4096-服务器确认 4094。
没关系,该服务器没有对第二个请求发送任何响应。根据TCP 连接终止:
终止的一方不能再向连接发送任何数据,但另一方可以。终止方应继续读取数据,直到另一方也终止。
下一个问题是为什么客户端收到服务器的关闭连接信号后会出现 4093 错误?
这可能是回答:
我想说的是 POST 与 FIN 同时发生,即客户端发送 POST 是因为其 TCP 堆栈尚未处理来自服务器的 FIN。请注意,数据包捕获是在系统处理数据之前完成的。
我无法对此发表评论,因为我不是网络专家。也许其他人可以给出更深刻的答案,说明为什么发送了第二个请求。
更新型多巴胺之前链接的问题不相关。问单独问题关于这个问题。
这个问题有解决办法吗?
正如在邮件列表:
HTTP/1.1 客户端需要处理保持连接关闭,所以这应该不是问题。
我认为应该在客户端处理。如果服务器关闭了连接,客户端应该打开新连接并重试请求。
我实际上需要一个负载平衡器,可以动态地添加和删除服务器,有没有更好的解决方案可以解决我的问题?
我不知道其他服务器的情况,所以无法在此提供建议。
只要您的客户端可以正确处理连接关闭,就没有任何理由阻止您使用 nginx。