簡體   English   中英

停止接受新的TCP連接,而不丟棄任何現有的TCP連接

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

我有兩個服務器在負載均衡器后面的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進行新連接或讓它們超時),以便負載均衡器可以將它們重新路由到另一台服務器,但同時又不會掉線任何已經建立的連接。

編輯:我正在尋找一種自動化的解決方案,它不需要我每次要更新應用程序時都更改任何負載平衡器或OS設置。

您可以在服務器上添加防火牆規則,該規則將阻止新的規則,但使舊的連接保持活動狀態。 我猜服務器是基於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>

ServerSocket.accept()阻塞或ServerSocketChannel.accept()返回null的任何時候,積壓隊列為空。 此時 ,停止接受並關閉監聽套接字。 等待所有現有的已接受套接字完成其工作,然后讓應用程序退出。

解決問題的最簡單方法是在應用程序服務器之前將其他負載平衡器放在本地。

檢查nginxHAproxy並選擇它們,這對您的任務來說更好。 它們都具有正常關閉的功能,這意味着它們將停止接受新連接,但將繼續為現有連接提供服務。 另一個優點是您的應用程序不需要任何代碼更改。

對於正常關機nginx

nginx -s quit

HAproxy關機:

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

我得出的結論是,我要實現的目標在Linux上是不可能的。 問題是操作系統通過發送SYN,ACKACK數據包完成了與客戶端的初始握手,而應用程序對此過程沒有任何控制。 握手后,連接建立,操作系統將其放入積壓隊列。 建立連接后,無論我在哪種情況下進行健康檢查,我正在使用的負載均衡器(F5 BigIP)在任何情況下都不會將其轉發到另一台服務器。 當我關閉套接字時,來自待辦事項隊列的已建立但尚未接受的連接被刪除。

但是,可以使用Windows套接字C ++ API的SO_CONDITIONAL_ACCEPT套接字選項和WSAAccept函數在Windows中實現。 此選項允許應用程序控制初始握手。 一個很好的解釋可以在這個答案中找到:

在端口上調用listen()時,操作系統開始接受該端口上的連接。 這意味着無論C代碼是否已調用accept(),它都開始向連接回復SYN,ACK數據包。 ...但是,在Windows上,SO_CONDITIONAL_ACCEPT調用使應用程序可以控制積壓隊列。 這意味着在應用程序對連接進行任何操作之前,服務器不會對SYN數據包做出任何答復。 這意味着,拒絕此級別的連接實際上可以將RST數據包發送到網絡,而無需創建狀態。

看起來Linux 沒有類似的功能 ,如以下答案所述

三向握手是tcp / ip基本結構的一部分,因此它已嵌入到堆棧中(即內核級別)。 握手后,您獲得的所有非內核代碼都會起作用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM