精品一区二区三区在线成人,欧美精产国品一二三区,Ji大巴进入女人66h,亚洲春色在线视频

徹底解決SpringCloud TCP連接過多未釋放問題!

網絡 網絡優化
為實現數據的可靠傳輸,TCP要在應用進程間建立傳輸連接。它是在兩個傳輸用戶之間建立一種邏輯聯系,使得通信雙方都確認對方為自己的傳輸連接端點。

參與項目中使用springcloud gateway,并結合nacos,做網關實現路由轉發以及負載均衡的功能。項目運行一段時間后,出現springcloud gateway服務的TCP連接過多未釋放的問題,針對此問題,查閱相關資料,匯總TCP連接、Http Keep-Alive和Tcp Keepalive的基礎知識,并結合操作系統windows和linux對keepalive參數的設置問題,最后落到部分常見的應用服務對keepalive參數的配置上,并以此為基礎,實踐調整springcloud gateway設置keepalive參數并進行了驗證有效,其他有關操作系統以及Nginx與Tomcat的設置問題,尚需后續實踐過程中進行驗證。于此進行記錄,以便后續繼續研究與驗證,也為后來者提供參考借鑒,文中不免疏漏之處,望讀者予以指正,不勝感激!

1.TCP連接介紹

為實現數據的可靠傳輸,TCP要在應用進程間建立傳輸連接。它是在兩個傳輸用戶之間建立一種邏輯聯系,使得通信雙方都確認對方為自己的傳輸連接端點。

1.1 建立連接—三次握手

建立連接前,服務器端首先被動打開其熟知的端口,對端口進行偵聽。當客戶端要和服務器端建立連接時,發起一個主動打開端口的請求(該端口一般為臨時端口);然后進入三次握手的過程。

圖片圖片

第一次握手:建立連接時,客戶端發送SYN包(seq=x)到服務器,并進入 SYN_SEND 狀態,等待服務器確認;

第二次握手:服務器收到SYN包,必須確認客戶的 SYN(ack=x+1),同時自己也發送一個 SYN 包(seq=y),即 SYN+ACK 包,此時服務器進入 SYN_RECV 狀態;

第三次握手:客戶端收到服務器的 SYN+ACK 包,向服務器發送確認包 ACK(ack=y+1),此包發送完畢,客戶端和服務器進入 ESTABLISHED 狀態,完成三次握手。

1.2 釋放連接—四次揮手

圖片圖片

  • 第一次揮手:Client發送一個FIN,用來關閉Client到Server的數據傳送,Client進入FIN_WAIT_1狀態。
  • 第二次揮手:Server收到FIN后,發送一個ACK給Client,確認序號為收到序號+1(與SYN相同,一個FIN占用一個序號),Server進入CLOSE_WAIT狀態。
  • 第三次揮手:Server發送一個FIN,用來關閉Server到Client的數據傳送,Server進入LAST_ACK狀態。
  • 第四次揮手:Client收到FIN后,Client進入TIME_WAIT狀態,接著發送一個ACK給Server,確認序號為收到序號+1, Server進入CLOSED狀態,完成四次揮手。

此時TCP連接還沒有釋放掉,必須經過時間等待計時器設置的時間2MSL后,Client才進入到連接關閉狀態。

說明:

2MSL即兩倍的MSL,TCP的TIME_WAIT狀態也稱為2MSL等待狀態,當TCP的一端發起主動關閉,在發出最后一個ACK包后,即第3次握手完成后發送了第四次握手的ACK包后就進入了TIME_WAIT狀態,必須在此狀態上停留兩倍的MSL時間,等待2MSL時間主要目的是怕最后一個ACK包對方沒收到,那么對方在超時后將重發第三次握手的FIN包,主動關閉端接到重發的FIN包后可以再發一個ACK應答包。在TIME_WAIT狀態 時兩端的端口不能使用,要等到2MSL時間結束才可繼續使用。當連接處于2MSL等待階段時任何遲到的報文段都將被丟棄。不過在實際應用中可以通過設置 SO_REUSEADDR選項達到不必等待2MSL時間結束再使用此端口。

如果有大量的連接,每次在連接、關閉時都要三次握手,四次揮手,會很明顯會造成性能低下。

2. KeepAlive與Keep-Alive介紹

TCP的KeepAlive和HTTP的Keep-Alive是完全不同的概念,不能混為一談。實際上HTTP的KeepAlive寫法是Keep-Alive,跟TCP的KeepAlive寫法上也有不同。

2.1 Http Keep-Alive

Http協議采用“請求-應答”模式,當使用普通模式,即非Keep-Alive模式時,每個請求/應答,客戶端和服務器都要新建一個連接,完成之后立即斷開連接;當使用Keep-Alive模式時,Keep-Alive功能使客戶端到服務器端的連接持續有效,當出現對服務器的后繼請求時,Keep-Alive功能避免了建立或者重新建立連接。

http1.0中默認是關閉的,需要在http頭加入”Connection: Keep-Alive”,才能啟用Keep-Alive;http 1.1中默認啟用Keep-Alive,如果加入”Connection: close “才關閉。目前大部分瀏覽器都是用http1.1協議,也就是說默認都會發起Keep-Alive的連接請求了,所以是否能完成一個完整的Keep- Alive連接就看服務器設置情況。

開啟Keep-Alive的優缺點:

? 優點:Keep-Alive模式更加高效,因為避免了連接建立和釋放的開銷。

? 缺點:長時間的Tcp連接容易導致系統資源無效占用,浪費系統資源。

2.2 Tcp KeepAlive

連接建立之后,如果客戶端一直不發送數據,或者隔很長時間才發送一次數據,當連接很久沒有數據報文傳輸時如何去確定對方還在線,到底是掉線了還是確實沒有數據傳輸,連接還需不需要保持,這種情況在TCP協議設計中是需要考慮到的。TCP協議通過一種巧妙的方式去解決這個問題,當超過一段時間之后,TCP自動發送一個數據為空的報文(偵測包)給對方,如果對方回應了這個報文,說明對方還在線,連接可以繼續保持,如果對方沒有報文返回,并且重試了多次之后則認為鏈接丟失,沒有必要保持連接。

tcp KeepAlive是TCP的一種檢測TCP連接狀況的保鮮機制。tcp KeepAlive保鮮定時器,支持三個系統內核配置參數:

net.ipv4.tcp_keepalive_intvl = 15  
net.ipv4.tcp_keepalive_probes = 5  
net.ipv4.tcp_keepalive_time = 1800

KeepAlive是TCP保鮮定時器,當網絡兩端建立了TCP連接之后,閑置(雙方沒有任何數據流發送往來)了tcp_keepalive_time后,服務器就會嘗試向客戶端發送偵測包,來判斷TCP連接狀況(有可能客戶端崩潰、強制關閉了應用、主機不可達等等)。如果沒有收到對方的回答(ack包),則會在 tcp_keepalive_intvl后再次嘗試發送偵測包,直到收到對方的ack,如果一直沒有收到對方的ack,一共會嘗試 tcp_keepalive_probes次,每次的間隔時間在這里分別是15s, 30s, 45s, 60s, 75s。如果嘗試tcp_keepalive_probes,依然沒有收到對方的ack包,則會丟棄該TCP連接。TCP連接默認閑置時間是2小時,一般設置為30分鐘足夠了。

3.操作系統有關Keepalive參數設置

3.1 Linux系統

  • tcp_keepalive_time 7200// 距離上次傳送數據多少時間未收到新報文判斷為開始檢測,單位秒,默認7200s(沒必要頻繁,浪費資源)。
  • tcp_keepalive_intvl 75// 檢測開始每多少時間發送心跳包,單位秒,默認75s。
  • tcp_keepalive_probes 9// 發送幾次心跳包對方未響應則close連接,默認9次。可通過對下列對應的配置文件進行修改參數:/proc/sys/net/ipv4/tcp_keepalive_time/proc/sys/net/ipv4/tcp_keepalive_intvl/proc/sys/net/ipv4/tcp_keepalive_probes

3.2 Windows系統

  • KeepAliveTimeKeepAliveTime的值控制系統嘗試驗證空閑連接是否仍然完好的頻率。如果該連接在一段時間內沒有活動,那么系統會發送保持連接的信號,如果網絡正常并且接收方是活動的,它就會響應。如果需要對丟失接收方的情況敏感,也就是說需要更快地發現是否丟失了接收方,請考慮減小該值。而如果長期不活動的空閑連接的出現次數較多,但丟失接收方的情況出現較少,那么可能需要增大該值以減少開銷。缺省情況下,如果空閑連接在7200000毫秒(2小時)內沒有活動,系統就會發送保持連接的消息。 通常建議把該值設為1800000毫秒,從而丟失的連接會在30分鐘內被檢測到。具體操作:瀏覽至HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters注冊表子鍵,在Parameters子鍵下創建或修改名為KeepAliveTime的REG_DWORD值,為該值設置適當的毫秒數。
  • KeepAliveIntervalKeepAliveInterval的值表示未收到另一方對“保持連接”信號的響應時,系統重復發送“保持連接”信號的頻率。在無任何響應的情況下,連續發送“保持連接”信號的次數超過TcpMaxDataRetransmissions(下文將介紹)的值時,將放棄該連接。如果網絡環境較差,允許較長的響應時間,則考慮增大該值以減少開銷;如果需要盡快驗證是否已丟失接收方,則考慮減小該值或TcpMaxDataRetransmissions值。缺省情況下,在未收到響應而重新發送“保持連接”的信號之前,系統會等待1000毫秒(1秒),可以根據具體需求修改,具體操作:瀏覽至HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\TCPIP\Parameters注冊表子鍵,在Parameters子鍵下創建或修改名為KeepAliveInterval的REG_DWORD值,為該值設置適當的毫秒數。

4.常用服務端配置Keepalive參數

4.1 Nginx設置Keepalive

當使用nginx作為反向代理時,為了支持長連接,需要做到兩點:

  • 從client到nginx的連接是長連接
  • 從nginx到server的連接是長連接

4.1.1 從 Client 到 Nginx 的連接是長連接

http {
    # 客戶端連接的超時時間, 為 0 時禁用長連接。 tcp連接在傳送完最后一個響應后,還需要hold住 keepalive_timeout秒后仍沒有新的http請求,才開始關閉這個連接
    keepalive_timeout 120s;
    # 在一個長連接上可以服務的最大請求數目, 當達到最大請求數目且所有已有請求結束后, 連接被關閉, 默認為 100, 即每個連接的最大請求數
    keepalive_request 10000;
  }

keepalive_requests:參數的真實含義,是指一個keepalive建立之后,nginx就會為這個連接設置一個計數器,記錄這個keep alive的長連接上已經接收并處理的客戶端請求的數量。如果達到這個參數設置的最大值時,則nginx會強行關閉這個長連接,逼迫客戶端不得不重新建立新的長連接。大多數情況下當QPS(每秒請求數)不是很高時,默認值100湊合夠用。但是,對于一些QPS比較高(比如超過10000QPS,甚至達到30000,50000甚至更高) 的場景,默認的100就顯得太低。簡單計算一下,QPS=10000時,客戶端每秒發送10000個請求(通常建立有多個長連接),每個連接只能最多跑100次請求,意味著平均每秒鐘就會有100個長連接因此被nginx關閉。同樣意味著為了保持QPS,客戶端不得不每秒中重新新建100個連接。因此,就會發現有大量的TIME_WAIT的socket連接(即使此時keep alive已經在client和nginx之間生效)。因此對于QPS較高的場景,非常有必要加大這個參數,以避免出現大量連接被生成再拋棄的情況,減少TIME_WAIT。

4.1.2 從 Nginx 到 Server(upstream) 的長連接

為了讓nginx和后端server(nginx稱為upstream)之間保持長連接,典型設置如下:(默認nginx訪問后端都是用的短連接(HTTP1.0),一個請求來了,Nginx 新開一個端口和后端建立連接,后端執行完畢后主動關閉該鏈接)

http {
    upstream  BACKEND {
        server   192.168.0.1:8080  weight=1 max_fails=2 fail_timeout=30s;
        server   192.168.0.2:8080  weight=1 max_fails=2 fail_timeout=30s;
        keepalive 300;        // 這個很重要!
    }
server {
        listen 8080 default_server;
        server_name "";
        location /  {
            proxy_pass http://BACKEND;
            proxy_http_version 1.1;         // 這兩個最好也設置
            proxy_set_header Connection "";
        }
    }
}

此處keepalive的含義不是開啟、關閉長連接的開關;也不是用來設置超時的timeout;更不是設置長連接池最大連接數。官方解釋:The connections parameter sets the maximum number of idle keepalive connections to upstream servers connections(設置到upstream服務器的空閑keepalive連接的最大數量) When this number is exceeded, the least recently used connections are closed. (當這個數量被突破時,最近使用最少的連接將被關閉) It should be particularly noted that the keepalive directive does not limit the total number of connections to upstream servers that an nginx worker process can open.(特別提醒:keepalive指令不會限制一個nginx worker進程到upstream服務器連接的總數量)

keepalive: 這個參數是 nginx 連接后端的連接池中的最大空閑連接數, 比如: 設置為 300; 如果 nginx 為了滿足請求的 qps; 創建了 1000 個連接的連接池, 這個時候只有 500 個請求過來, 那么 1000- 500 = 500; 那么就會多出 500 個空閑的連接, 那么 500 > 300; 那么 nginx 就會根據這個配置; 斷開 200 個請求連接; 那么這個時候就只有 800 個連接的連接池, 如果下次過來了 1000 個請求, 那么 nginx 又會開始創建連接; 所有這個數值的配置要小心配置

4.1.3 Nginx出現大量TIME_WAIT的情況

1)導致 nginx端出現大量TIME_WAIT的情況有兩種:keepalive_requests設置比較小,高并發下超過此值后nginx會強制關閉和客戶端保持的keepalive長連接;(主動關閉連接后導致nginx出現TIME_WAIT)keepalive設置的比較小(空閑數太小),導致高并發下nginx會頻繁出現連接數震蕩(超過該值會關閉連接),不停的關閉、開啟和后端server保持的keepalive長連接;

2)導致后端server端出現大量TIME_WAIT的情況:nginx沒有打開和后端的長連接,即:沒有設置proxy_http_version 1.1;和proxy_set_header Connection “”;從而導致后端server每次關閉連接,高并發下就會出現server端出現大量TIME_WAIT

4.2 Tomcat設置Keepalive

瀏覽器在請求的頭部添加 Connection:Keep-Alive,以此告訴服務器 “我支持長連接,你支持的話就和我建立長連接吧”,而倘若服務器的確支持長連接,那么就在響應頭部添加“Connection:Keep-Alive”,從而告訴瀏覽器“我的確也支持,那我們建立長連接吧”。服務器還可以通過 Keep-Alive:timeout=10, max=100 的頭部告訴瀏覽器“我希望 10 秒算超時時間,最長不能超過 100 秒”。

在 Tomcat 里是允許配置長連接的,配置 conf/server.xml 文件,配置 Connector 節點,該節點負責控制瀏覽器與 Tomcat 的連接,其中與長連接直接相關的有兩個屬性,它們分別是:keepAliveTimeout,它表示在 Connector 關閉連接前,Connector 為另外一個請求 Keep Alive 所等待的微妙數,默認值和 connectionTimeout 一樣;另一個是 maxKeepAliveRequests,它表示 HTTP/1.0 Keep Alive 和 HTTP/1.1 Keep Alive / Pipeline 的最大請求數目,如果設置為 1,將會禁用掉 Keep Alive 和 Pipeline,如果設置為小于 0 的數,Keep Alive 的最大請求數將沒有限制。也就是說在 Tomcat 里,默認長連接是打開的,當我們想關閉長連接時,只要將 maxKeepAliveRequests 設置為 1 就可以。

Tomcat在server.xml 中的Connector 元素中:

  • ? keepAliveTimeout:單位是milliseconds,表示在下次請求過來之前,tomcat保持該連接多久。這就是說假如客戶端不斷有請求過來,且未超過過期時間,則該連接將一直保持。
  • ? maxKeepAliveRequests:最大長連接個數(1表示禁用,-1表示不限制個數,默認100個。一般設置在100~200之間),表示該連接最大支持的請求數。超過該請求數的連接也將被關閉(此時就會返回一個Connection: close頭給客戶端)。

4.3 Netty設置Keepalive

4.3.1 SO_KEEPALIVE

Socket參數。是否啟用心跳保活機制,即連接保活。 啟用該功能時,TCP會主動探測空閑連接的有效性。

在雙方TCP套接字建立連接后(即都進入ESTABLISHED狀態)并且在兩個小時左右(默認的心跳間隔是7200s即2小時)上層沒有任何數據傳輸的情況下,這套機制才會被激活。

默認值:Netty默認關閉該功能,即值為:false 。

代碼設置:

bootstrap.childOption(ChannelOption.SO_KEEPALIVE, true);

說明:

  • ? 如果一方已經關閉或異常終止連接,而另一方卻不知道,我們將這樣的TCP連接稱為半打開的。TCP通過保活定時器(KeepAlive)來檢測半打開連接。
  • ? 在高并發的網絡服務器中,經常會出現漏掉socket的情況,對應的結果有一種情況就是出現大量的CLOSE_WAIT狀態的連接。這個時候,可以通過設置KEEPALIVE選項來解決這個問題。
  • ? 設置SO_KEEPALIVE選項來開啟KEEPALIVE,然后通過TCP_KEEPIDLE、TCP_KEEPINTVL和TCP_KEEPCNT設置keepalive的開始時間、間隔、次數等參數。
  • ? 當然,也可以通過設置/proc/sys/net/ipv4/tcp_keepalive_time、tcp_keepalive_intvl和tcp_keepalive_probes等內核參數來達到目的,但是這樣的話,會影響所有的socket。

4.3.2 SpringCloud Gateway設置Keepalive

@Configuration
public class NettyConfig {
    @Bean
    public NettyServerCustomizer nettyServerCustomizer() {
        return httpServer -> httpServer.tcpConfiguration(tcpServer -> {
            tcpServer= tcpServer.option(ChannelOption.SO_KEEPALIVE, true);
            tcpServer = tcpServer.doOnBind(serverBootstrap ->
                    BootstrapHandlers.updateConfiguration(serverBootstrap, "channelIdle", (connectionObserver, channel) -> {
                        ChannelPipeline pipeline = channel.pipeline();
                        pipeline.addLast(new ReadTimeoutHandler(5, TimeUnit.MINUTES));
                    }));
            return tcpServer;
        });
    }
}

5.參考資料

[1] https://www.cnblogs.com/xpfeia/p/10885726.html

[2] https://zhuanlan.zhihu.com/p/51560184

[3] https://blog.csdn.net/tennysonsky/article/details/45622395

[4] http://www.manongjc.com/detail/24-rcqeqovotuetucc.html

[5] https://www.jianshu.com/p/394a7883a139

[6] https://blog.csdn.net/bluetjs/article/details/80966148

[7] http://www.ttlsa.com/windows/parameter-optimization-of-tcp-under-windows-system/

[8] https://www.cnblogs.com/sunsky303/p/10648861.html

責任編輯:武曉燕 來源: JAVA日知錄
相關推薦

2021-12-03 12:15:01

QT中文亂碼Windows

2009-11-27 10:31:02

GPRS路由

2023-02-27 08:08:54

Pulsar源碼重復消費

2022-10-26 17:28:41

分布式事務seata

2025-03-03 00:13:50

2010-01-11 18:05:24

VB.NET窗體繼承

2010-01-04 15:05:53

2023-11-28 08:36:16

Spring中Body讀取

2025-06-17 06:40:45

DockerDocker鏡像

2009-12-25 09:39:08

ADSL MODEM

2010-01-14 10:19:05

2014-08-29 10:54:41

Oledb

2009-12-03 18:45:41

2009-11-24 19:50:10

2022-10-08 23:55:58

iOS蘋果開發

2009-12-03 16:54:04

無線寬帶路由器

2009-12-04 16:25:24

2020-09-28 14:41:24

Event Loop

2024-11-04 10:05:00

AI模型

2009-12-21 17:20:19

點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 香格里拉县| 都江堰市| 汨罗市| 茌平县| 福贡县| 延吉市| 洛川县| 连平县| 潮安县| 闵行区| 古田县| 满洲里市| 舟曲县| 蒙山县| 视频| 金堂县| 抚州市| 壶关县| 临夏市| 抚顺市| 安阳市| 仲巴县| 和平区| 资溪县| 福鼎市| 广州市| 平邑县| 金乡县| 宕昌县| 进贤县| 老河口市| 昌吉市| 鲁山县| 稻城县| 延寿县| 临邑县| 玛曲县| 亚东县| 和田县| 莫力| 迁西县|