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

Java 停止接受新的TCP连接而不删除任何现有连接,java,sockets,tcp,high-availability,Java,Sockets,Tcp,High Availability,我有两台服务器在负载平衡器后面的TCP端口上侦听。负载平衡器可以检测来自客户端的TCP连接尝试是否失败,并在不中断该连接的情况下重试到第二台服务器。我希望能够使这两台服务器中的任何一台停机进行维护,而无需删除单个客户端集合 我的服务器使用以下代码处理客户端请求: ServerSocketFactory ssf = ... ServerSocket serverSocket = ssf.createServerSocket(60000); try { while (true) {

我有两台服务器在负载平衡器后面的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中看到的,如果我在调用之前放置断点。 问题是在这一点上,只要我调用
serverSocket.close()
,所有这样的客户端连接都会断开。我想要实现的是告诉ServerSocket停止接受所有新连接的某种方式(即只发送RST以获取新连接或让它们超时),这样负载平衡器就可以将它们重新路由到另一台服务器,但同时不会丢弃任何已建立的连接


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

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

iptables -A INPUT -p tcp --syn --destination-port <port> -j REJECT --reject-with icmp-host-prohibited
并将其删除:

iptables -D INPUT <rule number>
iptables-D输入

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

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

选中
nginx
HAproxy
并选择其中一个,这对您的任务更有利。它们都有一个优雅关机的功能,这意味着它们停止接受新的连接,但继续为现有的连接服务到底。另一个优点是应用程序不需要对代码进行任何更改

nginx的正常关机:

nginx -s quit
HAproxy的正常关机:

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

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

<>但是,可以使用Windows套接字C++接口的套接字选项和函数来实现Windows。此选项允许应用程序控制初始握手。以下是一个很好的解释:

在端口上调用listen()时,操作系统开始接受连接 在那个港口。这意味着is开始向其回复SYN、ACK数据包 连接,不管C代码是否调用了accept()。 ... 但是,在windows上,SO_CONDITIONAL_ACCEPT调用允许 应用程序控制待办事项队列。这意味着 在应用程序启动之前,服务器不会对SYN数据包应答任何内容 对连接执行某些操作。这就是说, 此级别的连接实际上可以向网络发送RST数据包 不创建状态

它看起来像Linux,如中所述:

三方握手是tcp/ip基本结构的一部分,因此 它嵌入到堆栈中(即内核级)。所有非内核 你手上的代码在握手后进行操作


我相信这是loadbalancer的功能,它可以检测哪些服务器可以提供服务,并将新连接转发到该服务器。我不会让你的套接字代码更复杂,因为它不是它的函数。查看loadbalancer运行状况检查和路由配置。如果您从平衡器路由表中取出一个服务器ip,那么新的连接将转到另一个方向。是的,这是loadbalancer检测可用服务器并完成其工作的功能。它检测连接是否未建立(重置或超时),并将该连接转发到另一台服务器。问题是我的服务器在调用
serverSocket.close()
之前一直在接受新连接,即使我不想这样做。当我调用所有已经建立的连接时,
serverSocket.accept()
还没有被调用,这些连接就被删除了。在这种情况下,loadbalancer无法帮助我,因为它认为这些连接已建立,并假设服务器可以处理它们。这取决于loadbalancer的配置方式。我使用的loadbalancer(F5)具有运行状况检查配置。我们公开了一个servlet端点供LB监视。如果我们让这项服务不可用,LB会认为服务器已关闭,不会将新特拉福德路由到那里。我还与F5合作,它确实有健康检查。我使用,因此如果连接失败,它总是重试连接到另一台服务器。但它仍然不能解决上述问题,因为这些连接只有在建立之后才会失败。使用一些“每n秒检查一次”健康监视器而不是带内监视器甚至更糟糕,因为总有一个时间范围
nginx -s quit
haproxy -sf $(cat /var/run/haproxy.pid)