简体   繁体   English

停止接受新的TCP连接,而不丢弃任何现有的TCP连接

[英]Stop accepting new TCP connections without dropping any existing ones

I have two servers listening on a TCP port behind a load balancer. 我有两个服务器在负载均衡器后面的TCP端口上侦听。 The load balancer can detect if a TCP connection attempt from a client was unsuccessful and retry it to the second server without dropping that connection. 负载平衡器可以检测到来自客户端的TCP连接尝试是否失败,然后重试到第二台服务器,而不会断开该连接。 I want to be able to bring any of these two servers down for maintenance without dropping a single client collection. 我希望能够关闭这两个服务器中的任何一个以进行维护,而不必删除单个客户端集合。

My servers use this code to process client requests: 我的服务器使用以下代码来处理客户端请求:

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

My initial thought was to add a boolean that would be set on application shutdown and prevent new serverSocket.accept() calls while waiting for all existing connection to be processed and closed. 我最初的想法是添加一个将在应用程序关闭时设置的布尔值,并在等待所有现有连接被处理和关闭时阻止新的serverSocket.accept()调用。 However, new connection are being established even before the serverSocket.accept() call. 但是,甚至在serverSocket.accept()调用之前就已经建立了新的连接。 Here's what I see in Wireshark if I put a breakpoint before that call. 如果在该调用之前放置断点,这就是我在Wireshark中看到的内容。 在此处输入图片说明 The problem is at this point as soon as I call serverSocket.close() , all such client connections get dropped. 问题是在这一点上,只要我调用serverSocket.close() ,所有此类客户端连接都会被丢弃。 What I want to achieve is some way of telling ServerSocket to stop accept all new connections (ie only send RST for new connections or let them time out), so the load balancer can reroute them to another server, but at the same time not drop any already established connections. 我想要实现的是一种告诉ServerSocket停止接受所有新连接的方法(即仅发送RST进行新连接或让它们超时),以便负载均衡器可以将它们重新路由到另一台服务器,但同时又不会掉线任何已经建立的连接。

Edit: I'm looking for some automated solution which wouldn't require me to change any load balancer or OS settings every time I want to update the application. 编辑:我正在寻找一种自动化的解决方案,它不需要我每次要更新应用程序时都更改任何负载平衡器或OS设置。

You could add firewall rule on the server which will block new but keep old connections active. 您可以在服务器上添加防火墙规则,该规则将阻止新的规则,但使旧的连接保持活动状态。 I guess the server is Linux based? 我猜服务器是基于Linux的? If so, you could try with: 如果是这样,您可以尝试:

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

After that you can check with netstat is there any active connection and bring the application down once when there is no any: 之后,您可以使用netstat检查是否存在任何活动连接,并在没有任何活动时关闭应用程序一次:

netstat -ant|grep <port>|grep EST

After you finish with the maintenance, you can remove the firewall rule. 完成维护后,可以删除防火墙规则。 First, list all the rules to find it: 首先,列出所有规则以找到它:

iptables -L -n

And remove it: 并将其删除:

iptables -D INPUT <rule number>

At any point when ServerSocket.accept() blocks, or ServerSocketChannel.accept() returns null, the backlog queue is empty. ServerSocket.accept()阻塞或ServerSocketChannel.accept()返回null的任何时候,积压队列为空。 At that point , stop accepting and close the listening socket. 此时 ,停止接受并关闭监听套接字。 Wait for all existing accepted sockets to finish their work and let the application exit at that point. 等待所有现有的已接受套接字完成其工作,然后让应用程序退出。

The easiest way to solve your problem is to put additional load balancer locally right before your application server. 解决问题的最简单方法是在应用程序服务器之前将其他负载平衡器放在本地。

Check nginx and HAproxy and chose on of them, which is better for your task. 检查nginxHAproxy并选择它们,这对您的任务来说更好。 They both have a feature for graceful shutdown, which means that they stop accepting new connections but continue serving existing to the end. 它们都具有正常关闭的功能,这意味着它们将停止接受新连接,但将继续为现有连接提供服务。 Another advantage is that your application doesn't require any changes in code. 另一个优点是您的应用程序不需要任何代码更改。

Graceful shutdown for nginx : 对于正常关机nginx

nginx -s quit

Graceful shutdown for HAproxy : HAproxy关机:

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

I came to the conclusion that what I'm trying to achieve is not possible on Linux. 我得出的结论是,我要实现的目标在Linux上是不可能的。 The problem is the OS completes the initial handshake with the clients by sending the SYN,ACK and ACK packet, without any control over this process by the application. 问题是操作系统通过发送SYN,ACKACK数据包完成了与客户端的初始握手,而应用程序对此过程没有任何控制。 After the handshake, the connection becomes established and the OS puts it in the backlog queue. 握手后,连接建立,操作系统将其放入积压队列。 As soon as the connection is established, the load balancer that I'm using (F5 BigIP) doesn't forward it to another server under any circumstances, regardless of what kind of health checks I have there. 建立连接后,无论我在哪种情况下进行健康检查,我正在使用的负载均衡器(F5 BigIP)在任何情况下都不会将其转发到另一台服务器。 When I close the socket, already established but not yet accepted connections from the backlog queue got dropped. 当我关闭套接字时,来自待办事项队列的已建立但尚未接受的连接被删除。

However, it's possible to achieve with Windows using the SO_CONDITIONAL_ACCEPT socket option and WSAAccept function of the Windows Sockets C++ API. 但是,可以使用Windows套接字C ++ API的SO_CONDITIONAL_ACCEPT套接字选项和WSAAccept函数在Windows中实现。 This option allows the application to control the initial handshake. 此选项允许应用程序控制初始握手。 A good explanation can be found in this answer : 一个很好的解释可以在这个答案中找到:

When calling listen() on a port, the OS starts accepting connections on that port. 在端口上调用listen()时,操作系统开始接受该端口上的连接。 This means that is starts replying SYN,ACK packets to connections, regardless if the C code has called accept() yet. 这意味着无论C代码是否已调用accept(),它都开始向连接回复SYN,ACK数据包。 ... However, on windows, the SO_CONDITIONAL_ACCEPT call allows the application to take control of the backlog queue. ...但是,在Windows上,SO_CONDITIONAL_ACCEPT调用使应用程序可以控制积压队列。 This means that the server will not answer anything to a SYN packet until the application does something with the connection. 这意味着在应用程序对连接进行任何操作之前,服务器不会对SYN数据包做出任何答复。 This means, that rejecting connections at this level can actually send RST packets to the network without creating state. 这意味着,拒绝此级别的连接实际上可以将RST数据包发送到网络,而无需创建状态。

It looks like Linux doesn't have a similar feature , as described in this answer : 看起来Linux 没有类似的功能 ,如以下答案所述

The three-way handshake is a part of the basic structure of tcp/ip, so it's imbeded in the stack (ie kernel level). 三向握手是tcp / ip基本结构的一部分,因此它已嵌入到堆栈中(即内核级别)。 All the non-kernel code you get your hands on operate AFTER the handshake. 握手后,您获得的所有非内核代码都会起作用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 Restlet服务器不接受新连接 - New Connections are not accepting by Restlet Server 检查主机名和端口是否正确以及 postmaster 是否接受 TCP/IP 连接 - Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections 使用JBoss 4.2.x,如何在不取消部署现有数据源的情况下部署新数据源? - Using JBoss 4.2.x, how can I deploy a new data source without undeploying existing ones? 什么可以导致TCP / IP丢弃数据包而不丢弃连接? - What can cause TCP/IP to drop packets without dropping the connection? Hibernate:新的 Session 不掉表 - Hibernate: New Session without dropping the tables 与 127.0.0.1:5432 的连接被拒绝。 检查主机名和端口是否正确以及 postmaster 是否接受 TCP/IP 连接 - Connection to 127.0.0.1:5432 refused. Check that the hostname and port are correct and that the postmaster is accepting TCP/IP connections JBoss AS 7不接受远程连接 - JBoss AS 7 not accepting remote connections Apache Tomcat 7不接受连接 - Apache Tomcat 7 not accepting connections 有没有一种方法可以在Java中重用Socket来避免为新的TCP连接分配内存? - Is there a way to reuse Sockets in java to avoid allocating memory for new TCP connections? 如何阻止netty bootstrap监听和接受新的连接 - How to stop netty bootstrap from listening and accepting new connetions
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM