Java面試基礎(chǔ)之TCP連接以及其優(yōu)化
前言
作為一個后端程序員,網(wǎng)絡(luò)連接這塊是一個繞不過的砍,當(dāng)你在做服務(wù)器優(yōu)化的時候,網(wǎng)絡(luò)優(yōu)化也是其中一環(huán),那么作為網(wǎng)絡(luò)連接中最基礎(chǔ)的部分-TCP連接你了解嗎?今天我們來仔細(xì)看看這個部分。
TCP建立連接-三次握手
詳解
- 客戶端和服務(wù)器還未建立連接,但服務(wù)器一般處于listen狀態(tài)
- 客戶端主動建立連接,向服務(wù)器發(fā)送SYN報文,客戶端變?yōu)镾YN_SENT狀態(tài)
- 服務(wù)器收到客戶端發(fā)送的報文,也回了一個SYN報文,包含了一個ack。此時,服務(wù)器變?yōu)镾YN_RCVD狀態(tài)
- 客戶端收到了服務(wù)器發(fā)送的SYN報文,確認(rèn)了ack,它將向服務(wù)器發(fā)送一個ACK報文。此時,客戶端變?yōu)镋STABLISHED
- 服務(wù)器收到客戶端的ACK報文,確認(rèn)了ack。此時,服務(wù)器也變?yōu)镋STABLISHED
- 服務(wù)器和客戶端可以正常通信了
其中步驟2~4就是三次握手,那么為什么需要三次握手呢?為什么不是一次或者兩次握手呢?
首先,我們需要知道,只有當(dāng)服務(wù)器和客戶端都能確保自己能夠發(fā)消息和接收消息,這次網(wǎng)絡(luò)通信才算成功的。
步驟2的作用是讓服務(wù)器知道了自己是可以接收消息的。
步驟3的作用是讓客戶端知道自己發(fā)送消息和接收消息的功能是OK的,發(fā)送消息的能力是通過服務(wù)器返回的ack=x+1確認(rèn)的,因為這個值基于當(dāng)初客戶端發(fā)送的消息seq=x。接收消息的能力是因為收到了服務(wù)器的返回。
步驟4的作用是讓服務(wù)器端知道自己發(fā)送消息的能力是OK的(和步驟3類似)。
linux查看
linux服務(wù)器可以利用netstat -anp | grep tcp
命令,查看服務(wù)器上各個端口和應(yīng)用的連接狀態(tài)。
你還可以通過修改linux的配置文件/etc/sysctl.conf,調(diào)整各個狀態(tài)的數(shù)量
SYN_SENT狀態(tài)相關(guān)
主動建立連接時,發(fā)SYN(步驟2)的重試次數(shù)
nct.ipv4.tcp_syn_rctries = 6
建立連接時的本地端口可用范圍
net.ipv4.ip_local_port_range = 32768 60999
SYN_RCVD狀態(tài)相關(guān)
SYN_RCVD狀態(tài)連接的最大個數(shù)
net.ipv4.tcp_max_syn_backlog
被動建立連接時,發(fā)SYN/ACK(步驟3)重試次數(shù)
net.ipv4.tcp_synack_retries
說完了TCP建立連接,接下來,我們再來看看TCP正常斷開連接的過程
TCP斷開連接-四次揮手
詳解
- 客戶端與服務(wù)器端正常傳輸數(shù)據(jù)
- 客戶端主動斷開連接,向服務(wù)器端發(fā)送FIN報文,客戶端變?yōu)镕IN_WAIT1狀態(tài)
- 服務(wù)器收到客戶端的FIN后,向客戶端發(fā)送ACK報文,服務(wù)器變?yōu)镃LOSE_WAIT狀態(tài)
- 客戶端收到服務(wù)器的ACK報文后,客戶端變?yōu)镕IN_WAIT2狀態(tài)
- 服務(wù)器向客戶端發(fā)送FIN報文,服務(wù)器變?yōu)長AST_ACK狀態(tài)
- 客戶端收到服務(wù)器發(fā)送的FIN報文后,向服務(wù)器發(fā)送ACK報文,客戶端變?yōu)門IME_WAIT狀態(tài)
- 服務(wù)器收到客戶端的ACK報文后,服務(wù)器變?yōu)镃LOSED狀態(tài)
- 客戶端經(jīng)過2MSL(max segment lifetime,報文最大生存時間)時間后,也變?yōu)镃LOSED狀態(tài)
其中,步驟2、3、5、6即為4次揮手。
TIME_WAIT狀態(tài)及其優(yōu)化
看完之后,大家想必會有一個疑問,為什么TIME_WAIT狀態(tài)需要保持2MSL?因為這可以保證至少一次報文的往返時間內(nèi),端口是不可復(fù)用的。
假設(shè)TIME_WAIT狀態(tài)的持續(xù)時間很短,我們來模擬下面這種場景:
- 客戶端向服務(wù)器端發(fā)送了三條報文,其中第3條報文卡在網(wǎng)絡(luò)中,服務(wù)器只收到了前兩條,向客戶單發(fā)送ACK=2,客戶端重新發(fā)送第三條報文。
- 服務(wù)器主動發(fā)送FIN報文,客戶端收到后發(fā)送FIN、ACK,服務(wù)器端收到后發(fā)送ACK并進(jìn)入TIME_WAIT狀態(tài)(假設(shè)這個狀態(tài)很短)。
- 現(xiàn)在服務(wù)器又再次和客戶端建立連接,三次握手之后開始發(fā)送正常數(shù)據(jù),結(jié)果之前卡住的第三條報文,現(xiàn)在終于發(fā)送到服務(wù)器,但服務(wù)器也不知道該如何處理這條報文。
因此這也是TIME_WAIT狀態(tài)需要保持2MSL的原因,如果這么長時間也沒有收到報文,即使有正確的報文從客戶端發(fā)出,也已經(jīng)過期了,因此不會影響到之后的通信。
但這同樣也會帶來一個問題,TIME_WAIT狀態(tài)保持的時間較長,假設(shè)服務(wù)器端有大量TIME_WAIT狀態(tài)的TCP連接,就相當(dāng)于白白浪費掉大量的服務(wù)器資源(端口)。此時,我們可以通過修改以下配置進(jìn)行服務(wù)器調(diào)優(yōu):
net.ipv4.tcp_tw_reuse = 1
- 開啟后,作為客戶端時新連接可以使用仍然處于TIME_WAIT狀態(tài)的端口
- 由于timestamp的存在,操作系統(tǒng)可以拒絕遲到的報文(例如上面說的第三條報文),可以利用以下配置:
net.ipv4.tcp_timestamps = 1
其他狀態(tài)的優(yōu)化
CLOSE_WAIT狀態(tài)
如果服務(wù)器端有大量CLOSE_WAIT狀態(tài)的連接,很有可能是應(yīng)用進(jìn)程出現(xiàn)bug,沒有及時關(guān)閉連接。
FIN_WAIT1狀態(tài)
調(diào)整發(fā)送FIN報文的重試次數(shù),0相當(dāng)于8
net.ipv4.tcp_orphan_retries = 0
FIN_WAIT2狀態(tài)
調(diào)整保持在FIN_WAIT2狀態(tài)的時間
net.ipv4.tcp_fin_timeout = 60
總結(jié)
看到這里,想必你應(yīng)該對TCP連接有了一個大致的了解?,F(xiàn)在服務(wù)器大多都用了nginx做了負(fù)載均衡,因此,我們可能需要在此基礎(chǔ)上了解一些nginx相關(guān)的配置原理,這樣應(yīng)該會對我們的服務(wù)器性能調(diào)優(yōu)會有更大的幫助。
好了,以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。
相關(guān)文章
java 各種數(shù)據(jù)類型的互相轉(zhuǎn)換實例代碼
這篇文章主要介紹了java 各種數(shù)據(jù)類型的互相轉(zhuǎn)換實例代碼,需要的朋友可以參考下2020-10-10Spring?MVC如何實現(xiàn)接口Controller定義控制器
這篇文章主要介紹了Spring?MVC如何實現(xiàn)接口Controller定義控制器,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-02-02mybatis if test 不為空字符串或null的解決
這篇文章主要介紹了mybatis if test 不為空字符串或null的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-11-11Java中的String對象數(shù)據(jù)類型全面解析
首先String不屬于8種基本數(shù)據(jù)類型,String是一個對象,因為對象的默認(rèn)值是null,所以String的默認(rèn)值也是null;但它又是一種特殊的對象,有其它對象沒有的一些特性2012-11-11