停止接受新的TCP连接而不删除任何现有的连接

时间:2017-06-09 18:32:48

标签: java sockets tcp high-availability

我有两台服务器正在侦听负载均衡器后面的TCP端口。负载均衡器可以检测来自客户端的TCP连接尝试是否成功,并在不删除该连接的情况下将其重试到第二个服务器。我希望能够在不丢弃单个客户端集合的情况下将这两台服务器中的任何一台停机进行维护。

我的服务器使用此代码处理客户端请求:

ServerSocketFactory ssf = ...
ServerSocket serverSocket = ssf.createServerSocket(60000);
try {
    while (true) {
        Socket socket = serverSocket.accept();
        ...// Do the processing
    }
} catch (IOException e) {
    ...
}
...

我最初的想法是添加一个布尔值,该布尔值将在应用程序关闭时设置,并在等待处理和关闭所有现有连接时阻止新的serverSocket.accept()调用。但是,即使在serverSocket.accept()呼叫之前,也正在建立新的连接。这是我在Wireshark中看到的,如果我在该调用之前放置一个断点。 enter image description here 问题是,只要我调用serverSocket.close(),所有这些客户端连接都会被删除。我想要实现的是告诉ServerSocket停止接受所有新连接(即只为新连接发送RST或让它们超时)的一些方法,因此负载均衡器可以将它们重新路由到另一台服务器,但同时不会掉线任何已建立的联系。

编辑:我正在寻找一些自动解决方案,每次我想要更新应用程序时都不需要我更改任何负载均衡器或操作系统设置。

4 个答案:

答案 0 :(得分:4)

您可以在服务器上添加防火墙规则,该规则将阻止新的但保持旧连接处于活动状态。我猜服务器是基于Linux的?如果是这样,您可以尝试:

iptables -A INPUT -p tcp --syn --destination-port <port> -j REJECT --reject-with icmp-host-prohibited

之后您可以使用 netstat 查看是否存在任何活动连接,并在没有任何活动时将应用程序关闭一次:

netstat -ant|grep <port>|grep EST

完成维护后,您可以删除防火墙规则。首先,列出要找到它的所有规则:

iptables -L -n

删除它:

iptables -D INPUT <rule number>

答案 1 :(得分:3)

ServerSocket.accept()阻塞或ServerSocketChannel.accept()返回null的任何时候,积压队列都为空。 此时,停止接受并关闭侦听套接字。等待所有现有的已接受套接字完成其工作,然后让应用程序退出。

答案 2 :(得分:0)

解决问题的最简单方法是在应用程序服务器之前在本地放置额外的负载均衡器。

检查nginxHAproxy并选择它们,这对您的任务更有利。它们都具有正常关闭的功能,这意味着它们不再接受新连接,而是继续将现有服务提供给最终。另一个优点是您的应用程序不需要对代码进行任何更改。

正常关闭nginx

nginx -s quit

正常关闭HAproxy

haproxy -sf $(cat /var/run/haproxy.pid)

答案 3 :(得分:0)

我得出的结论是,我想要实现的目标在Linux上是不可能实现的。问题是操作系统通过发送 SYN,ACK ACK 数据包完成与客户端的初始握手,而不会由应用程序控制此过程。握手后,连接建立,操作系统将其置于积压队列中。一旦建立连接,我正在使用的负载均衡器(F5 BigIP)在任何情况下都不会将其转发到另一台服务器,无论我在那里进行何种健康检查。当我关闭套接字时,已经建立但尚未接受来自积压队列的连接被删除。

但是,使用Windows套接字C ++ API的SO_CONDITIONAL_ACCEPT套接字选项和WSAAccept功能可以实现Windows。此选项允许应用程序控制初始握手。可以在this answer中找到一个很好的解释:

  

在端口上调用listen()时,操作系统开始接受连接   在那个港口。这意味着开始回复SYN,ACK数据包   连接,无论C代码是否已调用accept()。   ...   但是,在Windows上,SO_CONDITIONAL_ACCEPT调用允许   应用程序来控制积压队列。这意味着   在应用程序之前,服务器不会回答SYN数据包的任何内容   通过连接做一些事情。这意味着,拒绝   此级别的连接实际上可以将RST数据包发送到网络   没有创造国家。

它看起来像Linux doesn't have a similar feature,如this answer

中所述
  

三次握手是tcp / ip基本结构的一部分,所以   它嵌入在堆栈中(即内核级别)。所有的非内核   在握手后你可以开始操作的代码。