1. 程式人生 > 其它 >技術分享| Linux高併發踩過的坑及效能優化

技術分享| Linux高併發踩過的坑及效能優化

Linux作業系統是現在伺服器的首選作業系統,在Linux的預設系統引數下,Linux針對高併發的支援性並不是很好。小編從事Linux下應用程式開發多年,關於Linux系統下的高併發,小編自己踩過的坑,及如何解決踩過的坑下面列上幾條,供大家參考,避免再次掉坑。

Linux應用執行過程中出現Too many open files 問題分析和解決

出現這句提示的原因是程式開啟的檔案socket連線數量超過系統設定值。

檢視每個使用者最大允許開啟的檔案數量

ulimit -a

其中 open files (-n) 1024 表示每個使用者最大允許開啟的檔案數量是1024

當前系統檔案控制代碼的最大數目,只用於檢視,不能設定修改

cat /proc/sys/fs/file-max

檢視某個程序的開啟檔案限制數

cat /proc/10446(pid)/limits

設定open files 數值方法

ulimit -n 65535 

這種設定方法在重啟後會還原為預設值。

永久設定方法:

vim /etc/security/limits.conf

在最後加入

* soft nofile 65535

* hard nofile 65535

生效需要重啟系統

這樣修改之後,問題得到有效解決。

Linux高併發下 time_wait 過多的問題分析及解決

現象是高併發場景下,伺服器執行應用卡頓。

排查方法:檢視伺服器配置:

netstat -ant|awk '/^tcp/ {++S[$NF]} END {for(a in S) print (a,S[a])}'

發現處於 time_wait 的數量太多,有幾萬條,應該是大量socket處於TIME_WAIT狀態。如果客戶端的併發量持續很高,此時部分客戶端就會顯示連線不上。
TCP連線狀態描述:

CLOSED:無連線是活動的或正在進行
LISTEN:伺服器在等待進入呼叫
SYN_RECV:一個連線請求已經到達,等待確認
SYN_SENT:應用已經開始,開啟一個連線
ESTABLISHED:正常資料傳輸狀態
FIN_WAIT1:應用說它已經完成
FIN_WAIT2:另一邊已同意釋放
ITMED_WAIT:等待所有分組死掉
CLOSING:兩邊同時嘗試關閉
TIME_WAIT:另一邊已初始化一個釋放
LAST_ACK:等待所有分組死掉

TIME_WAIT過多危害

網路情況不好時,如果主動方無TIME_WAIT等待,關閉前個連線後,主動方與被動方又建立起新的TCP連線,這時被動方重傳或延時過來的FIN包過來後會直接影響新的TCP連線;
同樣網路情況不好並且無TIME_WAIT等待,關閉連線後無新連線,當接收到被動方重傳或延遲的FIN包後,會給被動方回一個RST包,可能會影響被動方其它的服務連線。

針對如何解決TIME_WAIT 過多這一問題,解答如下:

編輯核心檔案/etc/sysctl.conf,加入以下內容:

net.ipv4.tcp_syncookies = 1 #表示開啟SYN Cookies。當出現SYN等待佇列溢位時,啟用cookies來處理,可防範少量SYN攻擊,預設為0,表示關閉;
net.ipv4.tcp_tw_reuse = 1 #表示開啟重用。允許將TIME-WAIT sockets重新用於新的TCP連線,預設為0,表示關閉;
net.ipv4.tcp_tw_recycle = 1 #表示開啟TCP連線中TIME-WAIT sockets的快速回收,預設為0,表示關閉。
net.ipv4.tcp_fin_timeout =30#修改系預設的 TIMEOUT 時間

然後執行 /sbin/sysctl -p 讓引數生效.

簡單來說,就是開啟系統的TIMEWAIT重用和快速回收。

Linux更多效能優化

如果您的系統的連線數本身就很多,如果以上配置調優後效能還不理想,可以再優化一下TCP的可使用埠範圍,進一步提升伺服器的併發能力。依然是/etc/sysctl.conf檔案中,加入下面這些配置:

vi /etc/sysctl.conf
#表示當keepalive起用的時候,TCP傳送keepalive訊息的頻度。預設是2小時,改為20分鐘。
net.ipv4.tcp_keepalive_time = 1200 
#表示用於向外連線的埠範圍。預設情況下很小:32768到61000,改為1024到65000。
net.ipv4.ip_local_port_range = 1024 65000 
#表示SYN佇列的長度,預設為1024,加大佇列長度為8192,可以容納更多等待連線的網路連線數。
net.ipv4.tcp_max_syn_backlog = 8192 
#表示系統同時保持TIME_WAIT套接字的最大數量,如果超過這個數字,TIME_WAIT套接字將立刻被清除並列印警告資訊。預設為180000,改為5000。對於Apache、Nginx等伺服器,上幾行的引數可以很好地減少TIME_WAIT套接字數量,但是對於 Squid,效果卻不大。此項引數可以控制TIME_WAIT套接字的最大數量,避免Squid伺服器被大量的TIME_WAIT套接字拖死。
net.ipv4.tcp_max_tw_buckets = 5000 

Linux核心更多引數優化說明

vim /etc/sysctl.conf

1、net.ipv4.tcp_max_syn_backlog = 65536

記錄的那些尚未收到客戶端確認資訊的連線請求的最大值。對於超過128M記憶體的系統而言,預設值是1024,低於128M小記憶體的系統則是128。

SYN Flood攻擊利用TCP協議散佈握手的缺陷,偽造虛假源IP地址傳送大量TCP-SYN半開啟連線到目標系統,最終導致目標系統Socket佇列資源耗盡而無法接受新的連線。為了應付這種攻擊,現代Unix系統中普遍採用多連線佇列處理的方式來緩衝(而不是解決)這種攻擊,是用一個基本佇列處理正常的完全連線應用(Connect()和Accept() ),是用另一個佇列單獨存放半開啟連線。

這種雙佇列處理方式和其他一些系統核心措施(例如Syn-Cookies/Caches)聯合應用時,能夠比較有效的緩解小規模的SYN Flood攻擊(事實證明<1000p/s)加大SYN佇列長度可以容納更多等待連線的網路連線數,一般遭受SYN Flood攻擊的網站,都存在大量SYN_RECV狀態,所以調大tcp_max_syn_backlog值能增加抵抗syn攻擊的能力。

2、net.core.netdev_max_backlog = 32768

每個網路介面接收資料包的速率比核心處理這些包的速率快時,允許送到佇列的資料包的最大數目。

3、net.core.somaxconn = 32768

調整系統同時發起併發TCP連線數,可能需要提高連線儲備值,以應對大量突發入局連線請求的情況。如果同時接收到大量連線請求,使用較大的值會提高受支援的暫掛連線的數量,從而可減少連線失敗的數量。大的偵聽佇列對防止DDoS攻擊也會有所幫助。掛起請求的最大數量預設是128。

檢視實時核心實時丟包命令:

netstat-su

位置:/proc/sys/

4、net.core.wmem_default = 8388608

該引數指定了傳送套接字緩衝區大小的預設值(以位元組為單位)

5、net.core.rmem_default = 8388608

該引數指定了接收套接字緩衝區大小的預設值(以位元組為單位)

6、net.core.rmem_max = 16777216

該引數指定了接收套接字緩衝區大小的最大值(以位元組為單位)

7、net.core.wmem_max = 16777216

該引數指定了傳送套接字緩衝區大小的最大值(以位元組為單位)

8、net.ipv4.tcp_timestamps = 0

Timestamps可以防範那些偽造的sequence號碼。一條1G的寬頻線路或許會重遇到帶out-of-line數值的舊sequence號碼(假如它是由於上次產生的)。時間戳能夠讓核心接受這種“異常”的資料包。這裡需要將其關掉,以提高效能。

9、net.ipv4.tcp_synack_retries = 2

對於遠端的連線請求SYN,核心會發送SYN+ACK資料報,以確認收到上一個SYN連線請求包。這是所謂的三次握手(threeway handshake)機制的第二個步驟。這裡決定核心在放棄連線之前所送出的SYN+ACK數目。不應該大於255,預設值是5,對應於180秒左右時間。(可以根據tcp_syn_retries來決定這個值)

10、net.ipv4.tcp_syn_retries = 2

對於一個新建連線,核心要傳送多少個SYN連線請求才決定放棄。不應該大於255,預設值是5,對應於180秒左右時間。(對於大負載而物理通訊良好的網路而言,這個值偏高,可修改為2.這個值僅僅是針對對外的連線,對進來的連線,是由tcp_retries1 決定的)

#net.ipv4.tcp_tw_len = 1

11、net.ipv4.tcp_tw_reuse = 1

表示開啟重用,允許將TIME-WAIT Sockets重新用於新的TCP連線,預設為0,表示關閉。這個對快速重啟動某些服務,而啟動後提示埠已經被使用的情形非常有幫助。

12、net.ipv4.tcp_mem = 94500000 915000000 927000000

tcp_mem有3個INTEGER變數:low, pressure, high

low:當TCP使用了低於該值的記憶體頁面數時,TCP沒有記憶體壓力,TCP不會考慮釋放記憶體。(理想情況下,這個值應與指定給tcp_wmem的第2個值相匹配。這第2個值表明,最大頁面大小乘以最大併發請求數除以頁大小 (131072*300/4096)

pressure:當TCP使用了超過該值的記憶體頁面數量時,TCP試圖穩定其記憶體使用,進入pressure模式,當記憶體消耗低於low值時則退出pressure狀態。(理想情況下這個值應該是TCP可以使用的總緩衝區大小的最大值(204800*300/4096)

high:允許所有TCP Sockets用於排隊緩衝資料報的頁面量。如果超過這個值,TCP連線將被拒絕,這就是為什麼不要令其過於保守(512000*300/4096)的原因了。在這種情況下,提供的價值很大,它能處理很多連線,是所預期的2.5倍;或者使現有連線能夠傳輸2.5倍的資料。

一般情況下這些值是在系統啟動時根據系統記憶體數量計算得到的。

13、net.ipv4.tcp_max_orphans = 3276800

系統所能處理不屬於任何程序的TCP sockets最大數量。假如超過這個數量﹐那麼不屬於任何程序的連線會被立即reset,並同時顯示警告資訊。之所以要設定這個限制﹐純粹為了抵禦那些簡單的DoS攻擊﹐千萬不要依賴這個或是人為的降低這個限制

14、net.ipv4.tcp_fin_timeout = 30

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

15、net.ipv4.ip_conntrack_max = 10000

設定系統對最大跟蹤的TCP連線數的限制(CentOS 5.6無此引數)

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

sysctlnet.ipv4.tcp_available_congestion_control

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

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

#設定TCP 擁塞演算法
net.ipv4.tcp_congestion_control=hybla

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

#開啟tcp_fastopen
net.ipv4.tcp_fastopen= 3

Iptables相關

如非必須,關掉或解除安裝iptables防火牆,並阻止kernel載入iptables模組。這些模組會影響併發效能。

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併發處理能力會顯著提高。上文所述配置僅供參考,用於生產環境請根據自己開發系統所部署的實際情況調整觀察再調整。

參考文章:

  1. 高併發Linux系統及kernel引數優化
  2. 優化Linux核心引數