Linux下偵聽(tīng)端口被占用,怎么解決?
本文轉(zhuǎn)載自微信公眾號(hào)「Linux開(kāi)發(fā)那些事兒」,作者LinuxThings。轉(zhuǎn)載本文請(qǐng)聯(lián)系Linux開(kāi)發(fā)那些事兒公眾號(hào)。
不知道你有沒(méi)有遇到過(guò)這種問(wèn)題:在同一臺(tái)物理機(jī)器上,服務(wù)A 啟動(dòng)時(shí)偵聽(tīng) 端口1 ,同時(shí)它也作為客戶端去連接 服務(wù)B,連接服務(wù)B時(shí)候會(huì)隨機(jī)一個(gè)端口號(hào),假如隨機(jī)的是 端口2 ,這個(gè)時(shí)候 服務(wù)C 正在啟動(dòng)中,它發(fā)現(xiàn)需要偵聽(tīng)的端口號(hào)已經(jīng)被 服務(wù)A的隨機(jī)端口 ( 端口號(hào)2 ) 占用了,導(dǎo)致服務(wù)C 啟動(dòng)失敗
上述的問(wèn)題是 服務(wù)器 偵聽(tīng)的端口 被客戶端隨機(jī)的端口給占用掉了,導(dǎo)致服務(wù)器無(wú)法啟動(dòng),接下來(lái)將介紹 這種情況出現(xiàn)的原因以及如何解決該問(wèn)題
如何隨機(jī)端口號(hào)
要弄清楚問(wèn)題的原因,先需要了解下系統(tǒng)是如何隨機(jī)端口號(hào)的
Linux 下 /proc/sys/net/ipv4/ip_local_port_range 定義了本地端口的范圍,此文件有兩個(gè)整形參數(shù),分別表示本地最小端口號(hào)和最大端口號(hào)
客戶端調(diào)用 connect 函數(shù)連接服務(wù)器的時(shí)候,操作系統(tǒng)會(huì)從本地端口范圍中隨機(jī)一個(gè)沒(méi)有使用的端口,作為本次連接的源端口號(hào),如果沒(méi)有可用的隨機(jī)端口,則連接出錯(cuò)
在早期的 Linux 版本中,本地端口范圍是從 1024 到 5000 ,總共有 3976 個(gè)端口可用,隨著網(wǎng)絡(luò)應(yīng)用的不斷增多,并發(fā)連接也會(huì)達(dá)到新的量級(jí),端口范圍可能會(huì)不夠用,所以,新的 Linux 版本調(diào)整了本地端口的范圍
下面是在 Linux 3.10 下查看的結(jié)果
- [root@cghost22 ~]# cat /proc/sys/net/ipv4/ip_local_port_range
- 32768 61000
注意:1024 以下的端口是系統(tǒng)保留端口,如果想修改 ip_local_port_range 中的端口, 確保都要大于1024
如何解決
通過(guò)對(duì)隨機(jī)端口的了解以及對(duì)問(wèn)題的分析,有以下幾種解決方案
- 調(diào)整服務(wù)器啟動(dòng)順序
前面提到,服務(wù)A 作為客戶端先連接 服務(wù)B,客戶端隨機(jī)的源端口 和 服務(wù)C 的偵聽(tīng)端口重復(fù)了,導(dǎo)致 服務(wù)C 偵聽(tīng)失敗
如果是這樣,那么調(diào)整下服務(wù)啟動(dòng)順序,讓 服務(wù)C 在 服務(wù)A 之前啟動(dòng),這樣就能保證 服務(wù)C 先偵聽(tīng)端口,后面 服務(wù)A 再隨機(jī)端口就不會(huì)和 服務(wù)C 的偵聽(tīng)端口 重復(fù)了
讓 某些服務(wù) 需要先于另一些 服務(wù) 啟動(dòng),會(huì)增加 服務(wù) 之間的耦合度,在設(shè)計(jì)中需要盡量避免這么做
退一步說(shuō),即使這么做了,如果后面 服務(wù)C 重啟,在它重啟的過(guò)程中,服務(wù)A 恰好正在重新連接 服務(wù)B, 此時(shí)還是有可能出現(xiàn) 服務(wù)A 隨機(jī)的端口 和 服務(wù)C 的 偵聽(tīng)端口重復(fù),當(dāng)該端口 先被 服務(wù)A 使用了,那么 服務(wù)C 還是會(huì)偵聽(tīng)失敗的
- 修改偵聽(tīng)端口
從上面 隨機(jī)端口 小節(jié)可知,偵聽(tīng)端口 和 隨機(jī)端口出現(xiàn)重復(fù)的原因是 偵聽(tīng)端口剛好處于 隨機(jī)端口的范圍中,所以隨機(jī)端口才有幾率出現(xiàn)和偵聽(tīng)端口相同
只要修改下 偵聽(tīng)端口 ,使之不會(huì)出現(xiàn)在 隨機(jī)端口范圍中,比如:把隨機(jī)端口范圍設(shè)置為 32768-61000,再把偵聽(tīng)端口設(shè)置為 1024-32767 之間的一個(gè)值, 這樣隨機(jī)端口 和 偵聽(tīng)端口 就不會(huì)出現(xiàn)重復(fù)了
如此做確實(shí)能解決問(wèn)題,但是 修改 服務(wù) 的偵聽(tīng)端口,跟該 服務(wù) 有邏輯關(guān)系上下游都需要做相應(yīng)的調(diào)整,如果項(xiàng)目已經(jīng)上線了,還需要走一套發(fā)布更新流程,需要花費(fèi)不少時(shí)間,如果項(xiàng)目還在開(kāi)發(fā)中,可以直接修改偵聽(tīng)端口
- 設(shè)置本地保留端口
這種方法是將 偵聽(tīng)端口 添加到 本地保留端口列表中,系統(tǒng)在隨機(jī)端口的時(shí)候,會(huì)過(guò)濾掉本地保留端口列表中配置的端口號(hào), 服務(wù)只需要做任何改動(dòng),只需要重啟下端口重復(fù)了的兩個(gè) 服務(wù) 即可
本地保留端口的配置位于 /proc/sys/net/ipv4/ip_local_reserved_ports , 它支持端口范圍以及單個(gè)端口號(hào)的配置,單個(gè)端口號(hào)之間用逗號(hào)分隔,端口范圍的最小值和最大值之間用 "-" 符號(hào)分隔
例如:50001,5200-5300,6001 表示 本地保留的是 5001,5200 到 5300,6001 這些端口
不過(guò),如果直接配置 /proc/sys/net/ipv4/ip_local_reserved_ports 文件的話,重啟機(jī)器之后配置會(huì)失效,要想重啟機(jī)器依然生效,直接配置 /etc/sysctl.conf
下面是配置本地保留的 5001,5200 到 5300,6001 端口的實(shí)例
用 vim 編輯 /etc/sysctl.conf ,在文件末尾添加以下行
- net.ipv4.ip_local_reserved_ports=5001,5200-5300,6001
再執(zhí)行 sysctl -p 命令重載 /etc/sysctl.conf 配置
小結(jié)
本文介紹了 Linux 下隨機(jī)端口和偵聽(tīng)端口重復(fù)的原因以及解決方法,雖然可以通過(guò)設(shè)置本地保留端口來(lái)解決,但這畢竟需要多一步配置操作,而且如果服務(wù)遷移到其他機(jī)器或者當(dāng)前機(jī)器上有服務(wù)使用新的偵聽(tīng)端口,還得添加到本地保留端口中,所以,這種方法比較適合線上緊急處理,比較推薦的做法還是修改下偵聽(tīng)端口,確保偵聽(tīng)端口不在隨機(jī)端口范圍內(nèi)