天天看點

高并發情況下Linux系統及kernel參數優化

  衆所周知在預設參數情況下Linux對高并發支援并不好,主要受限于單程序最大打開檔案數限制、核心TCP參數方面和IO事件配置設定機制等。下面就從幾方面來調整使Linux系統能夠支援高并發環境。

Iptables相關

  如非必須,關掉或解除安裝iptables防火牆,并阻止kernel加載iptables子產品。這些子產品會影響并發性能。

單程序最大打開檔案數限制

  一般的發行版,限制單程序最大可以打開1024個檔案,這是遠遠不能滿足高并發需求的,調整過程如下:

  在#号提示符下敲入:

<code># ulimit–n 65535</code>

将root啟動的單一程序的最大可以打開的檔案數設定為65535個。如果系統回顯類似于“Operationnotpermitted”之類的話,說明上述限制修改失敗,實際上是因為在中指定的數值超過了Linux系統對該使用者打開檔案數的軟限制或硬限制。是以,就需要修改Linux系統對使用者的關于打開檔案數的軟限制和硬限制。

第一步,修改limits.conf檔案,并添加:

<code># vim /etc/security/limits.conf</code>

<code>* softnofile 65536</code>

<code>* hard nofile65536</code>

  其中'*'号表示修改所有使用者的限制;soft或hard指定要修改軟限制還是硬限制;65536則指定了想要修改的新的限制值,即最大打開檔案數(請注意軟限制值要小于或等于硬限制)。修改完後儲存檔案。

第二步,修改/etc/pam.d/login檔案,在檔案中添加如下行:

<code># vim /etc/pam.d/login</code>

<code>sessionrequired </code><code>/lib/security/pam_limits</code><code>.so</code>

  這是告訴Linux在使用者完成系統登入後,應該調用pam_limits.so子產品來設定系統對該使用者可使用的各種資源數量的最大限制(包括使用者可打開的最大檔案數限制),而pam_limits.so子產品就會從/etc/security/limits.conf檔案中讀取配置來設定這些限制值。修改完後儲存此檔案。

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

<code># cat/proc/sys/fs/file-max</code>

<code>32568</code>

  這表明這台Linux系統最多允許同時打開(即包含所有使用者打開檔案數總和)32568個檔案,是Linux系統級硬限制,所有使用者級的打開檔案數限制都不應超過這個數值。通常這個系統級硬限制是Linux系統在啟動時根據系統硬體資源狀況計算出來的最佳的最大同時打開檔案數限制,如果沒有特殊需要,不應該修改此限制,除非想為使用者級打開檔案數限制設定超過此限制的值。修改此硬限制的方法是修改/etc/sysctl.conf檔案内fs.file-max= 131072

這是讓Linux在啟動完成後強行将系統級打開檔案數硬限制設定為131072。修改完後儲存此檔案。

  完成上述步驟後重新開機系統,一般情況下就可以将Linux系統對指定使用者的單一程序允許同時打開的最大檔案數限制設為指定的數值。如果重新開機後用ulimit-n指令檢視使用者可打開檔案數限制仍然低于上述步驟中設定的最大值,這可能是因為在使用者登入腳本/etc/profile中使用ulimit-n指令已經将使用者可同時打開的檔案數做了限制。由于通過ulimit-n修改系統對使用者可同時打開檔案的最大數限制時,新修改的值隻能小于或等于上次ulimit-n設定的值,是以想用此指令增大這個限制值是不可能的。是以,如果有上述問題存在,就隻能去打開/etc/profile腳本檔案,在檔案中查找是否使用了ulimit-n限制了使用者可同時打開的最大檔案數量,如果找到,則删除這行指令,或者将其設定的值改為合适的值,然後儲存檔案,使用者退出并重新登入系統即可。

  通過上述步驟,就為支援高并發TCP連接配接處理的通訊處理程式解除關于打開檔案數量方面的系統限制。

核心TCP參數方面

  Linux系統下,TCP連接配接斷開後,會以TIME_WAIT狀态保留一定的時間,然後才會釋放端口。當并發請求過多的時候,就會産生大量的TIME_WAIT狀态的連接配接,無法及時斷開的話,會占用大量的端口資源和伺服器資源。這個時候我們可以優化TCP的核心參數,來及時将TIME_WAIT狀态的端口清理掉。

  下面介紹的方法隻對擁有大量TIME_WAIT狀态的連接配接導緻系統資源消耗有效,如果不是這種情況下,效果可能不明顯。可以使用netstat指令去查TIME_WAIT狀态的連接配接狀态,輸入下面的組合指令,檢視目前TCP連接配接的狀态和對應的連接配接數量:

<code># netstat-n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’</code>

這個指令會輸出類似下面的結果:

<code>LAST_ACK16</code>

<code>SYN_RECV348</code>

<code>ESTABLISHED70</code>

<code>FIN_WAIT1229</code>

<code>FIN_WAIT230</code>

<code>CLOSING33</code>

<code>TIME_WAIT18098</code>

  我們隻用關心TIME_WAIT的個數,在這裡可以看到,有18000多個TIME_WAIT,這樣就占用了18000多個端口。要知道端口的數量隻有65535個,占用一個少一個,會嚴重的影響到後繼的新連接配接。這種情況下,我們就有必要調整下Linux的TCP核心參數,讓系統更快的釋放TIME_WAIT連接配接。

編輯配置檔案:/etc/sysctl.conf

<code># vim /etc/sysctl.conf</code>

在這個檔案中,加入下面的幾行内容:

<code>net.ipv4.tcp_syncookies= 1</code>

<code>net.ipv4.tcp_tw_reuse= 1</code>

<code>net.ipv4.tcp_tw_recycle= 1</code>

<code>net.ipv4.tcp_fin_timeout= 30</code>

輸入下面的指令,讓核心參數生效:

<code># sysctl-p</code>

簡單的說明上面的參數的含義:

net.ipv4.tcp_syncookies= 1

#表示開啟SYNCookies。當出現SYN等待隊列溢出時,啟用cookies來處理,可防範少量SYN攻擊,預設為0,表示關閉;

net.ipv4.tcp_tw_reuse= 1

#表示開啟重用。允許将TIME-WAITsockets重新用于新的TCP連接配接,預設為0,表示關閉;

net.ipv4.tcp_tw_recycle= 1

#表示開啟TCP連接配接中TIME-WAITsockets的快速回收,預設為0,表示關閉;

net.ipv4.tcp_fin_timeout

#修改系統預設的TIMEOUT 時間。

  在經過這樣的調整之後,除了會進一步提升伺服器的負載能力之外,還能夠防禦小流量程度的DoS、CC和SYN攻擊。

  此外,如果你的連接配接數本身就很多,我們可以再優化一下TCP的可使用端口範圍,進一步提升伺服器的并發能力。依然是往上面的參數檔案中,加入下面這些配置:

<code>net.ipv4.tcp_keepalive_time= 1200</code>

<code>net.ipv4.ip_local_port_range= 1024 65535</code>

<code>net.ipv4.tcp_max_syn_backlog= 8192</code>

<code>net.ipv4.tcp_max_tw_buckets= 5000</code>

  這幾個參數,建議隻在流量非常大的伺服器上開啟,會有顯著的效果。一般的流量小的伺服器上,沒有必要去設定這幾個參數。

#表示當keepalive起用的時候,TCP發送keepalive消息的頻度。預設是2小時,改為20分鐘。

#表示用于向外連接配接的端口範圍。預設情況下很小,改為1024到65535。

#表示SYN隊列的長度,預設為1024,加大隊列長度為8192,可以容納更多等待連接配接的網絡連接配接數。

#表示系統同時保持TIME_WAIT的最大數量,如果超過這個數字,TIME_WAIT将立刻被清除并列印警告資訊。預設為180000,改為5000。此項參數可以控制TIME_WAIT的最大數量,隻要超出了。

核心其他TCP參數說明:

<code>net.ipv4.tcp_max_syn_backlog= 65536</code>

#記錄的那些尚未收到用戶端确認資訊的連接配接請求的最大值。對于有128M記憶體的系統而言,預設值是1024,小記憶體的系統則是128。

<code>net.core.netdev_max_backlog= 32768</code>

#每個網絡接口接收資料包的速率比核心處理這些包的速率快時,允許送到隊列的資料包的最大數目。

<code>net.core.somaxconn= 32768</code>

#例如web應用中listen函數的backlog預設會給我們核心參數的net.core.somaxconn限制到128,而nginx定義的NGX_LISTEN_BACKLOG預設為511,是以有必要調整這個值。

<code>net.core.wmem_default= 8388608</code>

<code>net.core.rmem_default= 8388608</code>

<code>net.core.rmem_max= 16777216           </code><code>#最大socket讀buffer,可參考的優化值:873200</code>

<code>net.core.wmem_max= 16777216           </code><code>#最大socket寫buffer,可參考的優化值:873200</code>

<code>net.ipv4.tcp_timestsmps= 0</code>

#時間戳可以避免序列号的卷繞。一個1Gbps的鍊路肯定會遇到以前用過的序列号。時間戳能夠讓核心接受這種“異常”的資料包。這裡需要将其關掉。

<code>net.ipv4.tcp_synack_retries= 2</code>

#為了打開對端的連接配接,核心需要發送一個SYN并附帶一個回應前面一個SYN的ACK。也就是所謂三次握手中的第二次握手。這個設定決定了核心放棄連接配接之前發送SYN+ACK包的數量。

<code>net.ipv4.tcp_syn_retries= 2</code>

#在核心放棄建立連接配接之前發送SYN包的數量。

<code>#net.ipv4.tcp_tw_len= 1</code>

# 開啟重用。允許将TIME-WAITsockets重新用于新的TCP連接配接。

<code>net.ipv4.tcp_wmem= 8192 436600 873200</code>

# TCP寫buffer,可參考的優化值:8192 436600 873200

<code>net.ipv4.tcp_rmem  = 32768 436600 873200</code>

# TCP讀buffer,可參考的優化值:32768 436600 873200

<code>net.ipv4.tcp_mem= 94500000 91500000 92700000</code>

# 同樣有3個值,意思是:

net.ipv4.tcp_mem[0]:低于此值,TCP沒有記憶體壓力。

net.ipv4.tcp_mem[1]:在此值下,進入記憶體壓力階段。

net.ipv4.tcp_mem[2]:高于此值,TCP拒絕配置設定socket。

上述記憶體機關是頁,而不是位元組。可參考的優化值是:7864321048576 1572864

<code>net.ipv4.tcp_max_orphans= 3276800</code>

#系統中最多有多少個TCP套接字不被關聯到任何一個使用者檔案句柄上。

如果超過這個數字,連接配接将即刻被複位并列印出警告資訊。

這個限制僅僅是為了防止簡單的DoS攻擊,不能過分依靠它或者人為地減小這個值,

更應該增加這個值(如果增加了記憶體之後)。

#如果套接字由本端要求關閉,這個參數決定了它保持在FIN-WAIT-2狀态的時間。對端可以出錯并永遠不關閉連接配接,甚至意外當機。預設值是60秒。2.2 核心的通常值是180秒,你可以按這個設定,但要記住的是,即使你的機器是一個輕載的WEB伺服器,也有因為大量的死套接字而記憶體溢出的風險,FIN-WAIT-2的危險性比FIN-WAIT-1要小,因為它最多隻能吃掉1.5K記憶體,但是它們的生存期長些。

  同時還涉及到一個TCP 擁塞算法的問題,你可以用下面的指令檢視本機提供的擁塞算法控制子產品:

<code>sysctlnet.ipv4.tcp_available_congestion_control</code>

  對于幾種算法的分析,詳情可以參考下:TCP擁塞控制算法的優缺點、适用環境、性能分析,比如高延時可以試用hybla,中等延時可以試用htcp算法等。

如果想設定TCP 擁塞算法為hybla

<code>net.ipv4.tcp_congestion_control=hybla</code>

  額外的,對于核心版高于于3.7.1的,我們可以開啟tcp_fastopen:

net.ipv4.tcp_fastopen= 3

IO事件配置設定機制

  在Linux啟用高并發TCP連接配接,必須确認應用程式是否使用了合适的網絡I/O技術和I/O事件分派機制。可用的I/O技術有同步I/O,非阻塞式同步I/O,以及異步I/O。在高TCP并發的情形下,如果使用同步I/O,這會嚴重阻塞程式的運轉,除非為每個TCP連接配接的I/O建立一個線程。但是,過多的線程又會因系統對線程的排程造成巨大開銷。是以,在高TCP并發的情形下使用同步I/O是不可取的,這時可以考慮使用非阻塞式同步I/O或異步I/O。非阻塞式同步I/O的技術包括使用select(),poll(),epoll等機制。異步I/O的技術就是使用AIO。

  從I/O事件分派機制來看,使用select()是不合适的,因為它所支援的并發連接配接數有限(通常在1024個以内)。如果考慮性能,poll()也是不合适的,盡管它可以支援的較高的TCP并發數,但是由于其采用“輪詢”機制,當并發數較高時,其運作效率相當低,并可能存在I/O事件分派不均,導緻部分TCP連接配接上的I/O出現“饑餓”現象。而如果使用epoll或AIO,則沒有上述問題(早期Linux核心的AIO技術實作是通過在核心中為每個I/O請求建立一個線程來實作的,這種實作機制在高并發TCP連接配接的情形下使用其實也有嚴重的性能問題。但在最新的Linux核心中,AIO的實作已經得到改進)。

綜上所述,在開發支援高并發TCP連接配接的Linux應用程式時,應盡量使用epoll或AIO技術來實作并發的TCP連接配接上的I/O控制,這将為提升程式對高并發TCP連接配接的支援提供有效的I/O保證。

  經過這樣的優化配置之後,伺服器的TCP并發處理能力會顯著提高。以上配置僅供參考,用于生産環境請根據自己的實際情況調整觀察再調整。

<b>      本文轉自Jx戰壕  51CTO部落格,原文連結:http://blog.51cto.com/xujpxm/1958881</b><b>,如需轉載請自行聯系原作者</b>