參考:https://www.phpmianshi.com/?id=109
問題描述:
最近系統報警有類型如下錯誤:Cannot assign requested address 主要是連接mysql時產生的錯誤。
分析原因:
客戶端與服務端每建立一個連接,客戶端一側都會佔用一個本地端口(假設沒有啓用SO_REUSEADDR選項),本地端口數量是有限制的(默認是net.ipv4.ip_local_port_range=32768 61000),也就是說在沒設置socket的SO_REUSEADDR選項時,一臺Linux服務器作爲客戶端(注意是作爲客戶端)默認只能建立大概3萬個TCP連接(服務端沒有這個限制),可以更改net.ipv4.ip_local_port_range增大作爲客戶端可發起的併發連接數,但最多不會超過65535個(服務端沒有這個限制)。
當Linux服務器作爲客戶端頻繁建立TCP短連接時,本地會可能會產生很多TIME_WAIT狀態的連接,客戶端側的TIME_WAIT狀態的連接會佔用一個本地端口直到達到2MSL(最長分解生命期)的時間,這樣會導致本地端口被暫時佔用,當短連接建立速度過快時(例如做壓測時),會導致Cannot assign requested address錯誤
當系統的內核版本小於 3.2 時:
ip_local_port_range 決定了客戶端的一個 ip 可用的端口數量,即一個 ip 最多隻能創建61000-32768個連接,如果要突破這個限制需要客戶端機器綁定多個 ip
當系統的內核版本大於等於 3.2 時:
ip_local_port_range 決定的是 socket 四元組中的本地端口數量,即一個 ip 對同一個目標 ip+port 最多可以創建61000-32768個連接,只要目標 ip或端口不一樣就可以使用相同的本地端口,不一定需要多個客戶端 ip 就可以突破端口數量限制。
解決方法:
設置端口複用(複用TIME_WAIT狀態的連接,稍微調大端口範圍)。
打開文件 /etc/sysctl.conf,增加以下設置
net.ipv4.ip_forward = 1
net.ipv4.tcp_timestamps = 1
net.ipv4.tcp_tw_recycle=1
net.ipv4.tcp_tw_reuse = 1
net.ipv4.tcp_fin_timeout = 10
net.ipv4.ip_local_port_range = 20000 61000
net.ipv4.tcp_max_tw_buckets = 200000
net.nf_conntrack_max = 524288
net.netfilter.nf_conntrack_max = 524288
net.netfilter.nf_conntrack_tcp_timeout_close_wait = 60
net.netfilter.nf_conntrack_tcp_timeout_fin_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_time_wait = 120
net.netfilter.nf_conntrack_tcp_timeout_established = 3600
fs.file-max=100001
運行 sysctl -p即可生效
有可能產生的問題:
稍微調大端口範圍,有可能會跟系統服務預留的監聽端口衝突
我們可以將服務監聽的端口以逗號分隔全部添加到ip_local_reserved_ports中,TCP/IP協議棧從ip_local_port_range中隨機選取源端口時,會排除ip_local_reserved_ports中定義的端口,因此就不會出現端口被佔用了服務無法啓動。注意他的格式可以是這種:1,2-4,10-10 詳細介紹下面解釋。
2個參數的官方解釋:
ip_local_port_range - 2 INTEGERS Defines the local port range that is used by TCP and UDP to choose the local port. The first number is the first, the second the last local port number. If possible, it is better these numbers have different parity (one even and one odd value). Must be greater than or equal to ip_unprivileged_port_start. The default values are 32768 and 60999 respectively. ip_local_reserved_ports - list of comma separated ranges Specify the ports which are reserved for known third-party applications. These ports will not be used by automatic port assignments (e.g. when calling connect() or bind() with port number 0). Explicit port allocation behavior is unchanged. The format used for both input and output is a comma separated list of ranges (e.g. "1,2-4,10-10" for ports 1, 2, 3, 4 and 10). Writing to the file will clear all previously reserved ports and update the current list with the one given in the input. Note that ip_local_port_range and ip_local_reserved_ports settings are independent and both are considered by the kernel when determining which ports are available for automatic port assignments. You can reserve ports which are not in the current ip_local_port_range, e.g.: $ cat /proc/sys/net/ipv4/ip_local_port_range 32000 60999 $ cat /proc/sys/net/ipv4/ip_local_reserved_ports 8080,9148 although this is redundant. However such a setting is useful if later the port range is changed to a value that will include the reserved ports. Default: Empty