天天看點

Linux 下 socket 高并發分析與優化

工作環境

作業系統:Linux ( Ubuntu server 12.04 / 64 bit)

核心版:Linux Ubuntu-Server 3.13.0-32-generic

1:連接配接限制

- 檔案句柄限制

在 linux 下網絡程式設計每一個 tcp 連接配接都要占一個檔案描述符,一旦這個檔案描述符使用完了,新的連接配接到來傳回給我們的錯誤是“Socket/File:Can’t open so many files”。

這時需要明白作業系統對可以打開的最大檔案數的限制。

  • 程序限制

    檢視系統允許目前使用者程序打開的檔案數限制

ulimit -n
//預設輸出 1024,說明對于一個程序而言最多隻能打開1024個檔案,是以如果采用此預設配置單程序最多可以并發上千個 TCP 連接配接。
           
1. 修改/etc/security/limits.conf檔案,在檔案中添加如下行:
           
* soft nofile 204800
* hard nofile 204800
// * 代表所有使用者
           
2. 修改/etc/pam.d/login檔案,在檔案中添加如下行:
           
session required /lib/x86_64-linux-gnu/security/pam_limits.so
// 這是告訴Linux在使用者完成系統登入後,應該調用pam_limits.so子產品來設定系統對該使用者可使用的各種資源數量
// 的最大限制(包括使用者可打開的最大檔案數限制),而pam_limits.so子產品就會從/etc/security/limits.conf
// 檔案中讀取配置來設定這些限制值。修改完後儲存此檔案。
           
  • 全局限制

檢視Linux系統級的最大打開檔案數限制,使用如下指令:

cat /proc/sys/fs/file-max
//顯示的是這台Linux系統最多允許同時打開(即包含所有使用者打開)檔案數總和,是Linux系統級硬限制,
//所有使用者級的打開檔案數限制都不應超過這個數值。通常這個系統級硬限制是Linux系統在啟動時根據系統硬體資源
//狀況計算出來的最佳的最大同時打開檔案數限制,如果沒有特殊需要,不應該修改此限制,除非想為使用者級打開檔案
//數限制設定超過此限制的值。
           
cat /proc/sys/fs/file-nr 
// 輸出 9344 0 592026,分别為:1.已經配置設定的檔案句柄數,2.已經配置設定但沒有使用的檔案句柄數,3.最大檔案句柄數。但在kernel 2.6版本中第二項的值總為0,這并不是一個錯誤,它實際上意味着已經配置設定的檔案描述符無一浪費的都已經被使用了 。
           

用 root 權限修改 /etc/sysctl.conf 檔案,把這個數值改大些:

net.nf_conntrack_max = 
net.netfilter.nf_conntrack_max = 
           
/sbin/sysctl -p //立即生效
/sbin/sysctl net.netfilter.nf_conntrack_max // 驗證是否生效
           

- 端口号限制

作業系統上端口号1024以下是系統保留的,從1024-65535是使用者使用的。

  • 如何辨別一個TCP連接配接:系統用一個4四元組來唯一辨別一個TCP連接配接:

    {local ip, local port,remote ip,remote port}。

    第一、二個參數代表了服務端的ip位址和端口号,第三、四個參數代表了用戶端的ip位址和端口号。而我們作為服務端實際隻使用了bind時這一個端口,說明端口号65535并不是并發量的限制。

  • server最大tcp連接配接數:server通常固定在某個本地端口上監聽,等待client的連接配接請求。不考慮位址重用(unix的SO_REUSEADDR選項)的情況下,即使server端有多個ip,本地監聽端口也是獨占的,是以server端tcp連接配接4元組中隻有remote ip(也就是client ip)和remote port(用戶端port)是可變的,是以最大tcp連接配接為用戶端ip數×用戶端port數,對IPV4,不考慮ip位址分類等因素,最大tcp連接配接數約為2的32次方(ip數)×2的16次方(port數),也就是server端單機最大tcp連接配接數約為2的48次方。

2:核心優化

etc/sysctl.conf 是用來控制linux網絡的配置檔案,對于依賴網絡的程式(如web伺服器和cache伺服器)非常重要。核心參數sysctl.conf的優化:

net.ipv4.tcp_max_tw_buckets = 
net.ipv4.ip_local_port_range =  
net.ipv4.tcp_tw_recycle = 
net.ipv4.tcp_tw_reuse = 
net.ipv4.tcp_syncookies = 
net.ipv4.tcp_synack_retries = 
net.ipv4.tcp_syn_retries = 
net.ipv4.tcp_max_orphans = 
net.ipv4.tcp_max_syn_backlog = 
net.ipv4.tcp_rmem =   
net.ipv4.tcp_wmem =   
net.ipv4.tcp_mem =   
net.ipv4.tcp_fin_timeout = 
net.ipv4.tcp_keepalive_time = 
net.core.somaxconn = 
net.core.netdev_max_backlog = 
net.core.wmem_default = 
net.core.rmem_default = 
net.core.rmem_max = 
net.core.wmem_max = 
net.nf_conntrack_max = 
net.netfilter.nf_conntrack_max = 
           

另外,在此優化過程中可能會有報錯:

error: "net.ipv4.ip_conntrack_max"is an unknown key
error: "net.ipv4.netfilter.ip_conntrack_max"is an unknown key
// 這個錯誤可能是你的防火牆沒有開啟或者自動處理可載入的子產品ip_conntrack沒有自動載入,解決辦法有二,一是開啟防火牆,二是自動處理開載入的子產品ip_conntrack
modprobe nf_conntrack
echo "modprobe nf_conntrack">> /etc/rc.local
           

注:在核心2.6之前的此項防火牆配置需要改為

net.ipv4.ip_conntrack_max = 
net.ipv4.netfilter.ip_conntrack_max = 

modprobe ip_conntrack
echo "modprobe ip_conntrack">> /etc/rc.local
           

參數解釋:

net.ipv4.tcp_max_tw_buckets =           
# 用來設定timewait的數量,預設是180 000,這裡設為6000。
net.ipv4.ip_local_port_range =     
# 用來設定允許系統打開的端口範圍。
net.ipv4.tcp_tw_recycle =                  
# 用于設定啟用timewait快速回收
net.ipv4.tcp_tw_reuse =                    
# 用于設定開啟重用,允許将TIME-WAIT sockets重新用于新的TCP連接配接
net.ipv4.tcp_syncookies =                  
# 用于設定開啟SYN Cookies,當出現SYN等待隊列溢出時,啟用cookies進行處理。
net.ipv4.tcp_synack_retries =              
# 參數的值決定了核心放棄連接配接之前發送SYN+ACK包的數量。
net.ipv4.tcp_syn_retries =                 
# 表示在核心放棄建立連接配接之前發送SYN包的數量。
net.ipv4.tcp_max_orphans =            
# 用于設定系統中最多有多少個TCP套接字不被關聯到任何一個使用者檔案句柄上。如果超過這個數字,孤立連接配接将立即
# 被複位并列印出警告資訊。這個限制隻是為了 防止簡單的DoS攻擊。不能過分依靠這個限制甚至人為減小這個值,
# 更多的情況下應該增加這個值。
net.ipv4.tcp_max_syn_backlog =        
# 用于記錄那些尚未收到用戶端确認資訊的連接配接請求的最大值。對于有128MB記憶體的系統而言,此參數的預設值是
# 1024,對小記憶體的系統則是128。
net.ipv4.tcp_rmem =        
# TCP讀buffer
net.ipv4.tcp_wmem =        
# TCP寫buffer
net.ipv4.tcp_mem =      
# 同樣有3個值,意思是:net.ipv4.tcp_mem[0]:低于此值,TCP沒有記憶體壓力。
# net.ipv4.tcp_mem[1]:在此值下,進入記憶體壓力階段。
# net.ipv4.tcp_mem[2]:高于此值,TCP拒絕配置設定socket。
net.ipv4.tcp_timestsmps =                  
# 時間戳可以避免序列号的卷繞。
net.ipv4.tcp_fin_timeout =                
# 決定了套接字保持在FIN-WAIT-2狀态的時間。預設值是60秒。正确設定這個值非常重要,有時即使一個負載很小的
# Web伺服器,也會出現大量的死套接字而産生記憶體溢出的風險。
net.ipv4.tcp_keepalive_time =           
# 表示當keepalive啟用的時候,TCP發送keepalive消息的頻度。預設值是2(機關是小時)。1200/60 = 20 分鐘
net.core.somaxconn =                   
# 預設值是128, 這個參數用于調節系統同時發起的tcp連接配接數,在高并發的請求中,預設的值可能會導緻連結逾時或
# 者重傳,是以,需要結合并發請求數來調節此值。
net.core.netdev_max_backlog =         
# 表示當每個網絡接口接收資料包的速率比核心處理這些包的速率快時,允許發送到隊列的資料包的最大數目。
net.core.rmem_default =              
# 預設的接收視窗大小
net.core.wmem_default =              
# 預設的發送視窗大小
net.core.rmem_max =                 
# 最大socket接收緩沖
net.core.wmem_max =                 
# 最大socket發送緩沖
net.nf_conntrack_max =                
# 提升系統整體連接配接數
net.netfilter.nf_conntrack_max =      
# 提升系統整體連接配接數
           
/sbin/sysctl -p //立即生效
echo "/sbin/sysctl -p">> /etc/rc.local //重新開機後自動生效
           

繼續閱讀