眾所周知在默認引數情況下Linux對高并發支持并不好,主要受限于單行程最大打開檔案數限制、內核TCP引數方面和IO事件分配機制等,下面就從幾方面來調整使Linux系統能夠支持高并發環境,

iptables相關
如非必須,關掉或卸載iptables防火墻,并阻止kernel加載iptables模塊,這些模塊會影響并發性能,
單行程最大打開檔案數限制
一般的發行版,限制單行程最大可以打開1024個檔案,這是遠遠不能滿足高并發需求的,調整程序如下:
在#號提示符下敲入:
# ulimit –n 65535
將root啟動的單一行程的最大可以打開的檔案數設定為65535個,如果系統會顯類似于“Operationnotpermitted”之類的話,說明上述限制修改失敗,實際上是因為在中指定的數值超過了Linux系統對該用戶打開檔案數的軟限制或硬限制,因此,就需要修改Linux系統對用戶的關于打開檔案數的軟限制和硬限制,
第一步,修改limits.conf檔案,并添加:
# vim /etc/security/limits.conf* soft nofile 65535* hard nofile 65535
其中'*'號表示修改所有用戶的限制;soft或hard指定要修改軟限制還是硬限制;65536則指定了想要修改的新的限制值,即最大打開檔案數(請注意軟限制值要小于或等于硬限制),修改完后保存檔案,
第二步,修改/etc/pam.d/login檔案,在檔案中添加如下行:
# vim /etc/pam.d/loginsessionrequired /lib/security/pam_limits.so
這是告訴Linux在用戶完成系統登錄后,應該呼叫pam_limits.so模塊來設定系統對該用戶可使用的各種資源數量的最大限制(包括用戶可打開的最大檔案數限制),而pam_limits.so模塊就會從/etc/security/limits.conf檔案中讀取配置來設定這些限制值,修改完后保存此檔案,
第三步,查看Linux系統級的最大打開檔案數限制,使用如下命令:
# cat/proc/sys/fs/file-max32568
這表明這臺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連接的狀態和對應的連接數量:
# netstat-n | awk ‘/^tcp/ {++S[$NF]} END {for(a in S) print a, S[a]}’
這個命令會輸出類似下面的結果:
LAST_ACK16SYN_RECV348ESTABLISHED70FIN_WAIT1229FIN_WAIT230CLOSING33TIME_WAIT18098
我們只用關心TIME_WAIT的個數,在這里可以看到,有18000多個TIME_WAIT,這樣就占用了18000多個埠,要知道埠的數量只有65535個,占用一個少一個,會嚴重的影響到后繼的新連接,這種情況下,我們就有必要調整下Linux的TCP內核引數,讓系統更快的釋放TIME_WAIT連接,
編輯組態檔:/etc/sysctl.conf,在這個檔案中,加入下面的幾行內容:
# vim /etc/sysctl.confnet.ipv4.tcp_syncookies= 1net.ipv4.tcp_tw_reuse= 1net.ipv4.tcp_tw_recycle= 1net.ipv4.tcp_fin_timeout= 30
輸入下面的命令,讓內核引數生效:
# sysctl-p
簡單的說明上面的引數的含義:
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的可使用埠范圍,進一步提升服務器的并發能力,依然是往上面的引數檔案中,加入下面這些配置:
net.ipv4.tcp_keepalive_time= 1200net.ipv4.ip_local_port_range= 1024 65535net.ipv4.tcp_max_syn_backlog= 8192net.ipv4.tcp_max_tw_buckets= 5000
這幾個引數,建議只在流量非常大的服務器上開啟,會有顯著的效果,一般的流量小的服務器上,沒有必要去設定這幾個引數,
net.ipv4.tcp_keepalive_time= 1200
表示當keepalive起用的時候,TCP發送keepalive訊息的頻度,預設是2小時,改為20分鐘,
ip_local_port_range= 1024 65535
表示用于向外連接的埠范圍,預設情況下很小,改為1024到65535,
net.ipv4.tcp_max_syn_backlog= 8192
表示SYN佇列的長度,默認為1024,加大佇列長度為8192,可以容納更多等待連接的網路連接數,
net.ipv4.tcp_max_tw_buckets= 5000
表示系統同時保持TIME_WAIT的最大數量,如果超過這個數字,TIME_WAIT將立刻被清除并列印警告資訊,默認為180000,改為5000,此項引數可以控制TIME_WAIT的最大數量,只要超出了,
內核其他TCP引數說明
net.ipv4.tcp_max_syn_backlog= 65535
記錄的那些尚未收到客戶端確認資訊的連接請求的最大值,對于有128M記憶體的系統而言,預設值是1024,小記憶體的系統則是128,
net.core.netdev_max_backlog= 32768
每個網路介面接收資料包的速率比內核處理這些包的速率快時,允許送到佇列的資料包的最大數目,
net.core.somaxconn= 32768
例如web應用中listen函式的backlog默認會給我們內核引數的net.core.somaxconn限制到128,而nginx定義的NGX_LISTEN_BACKLOG默認為511,所以有必要調整這個值,
net.core.wmem_default= 8388608net.core.rmem_default= 8388608net.core.rmem_max= 16777216 #最大socket讀buffer,可參考的優化值:873200net.core.wmem_max= 16777216 #最大socket寫buffer,可參考的優化值:873200net.ipv4.tcp_timestsmps= 0
時間戳可以避免序列號的卷繞,一個1Gbps的鏈路肯定會遇到以前用過的序列號,時間戳能夠讓內核接受這種“例外”的資料包,這里需要將其關掉,
net.ipv4.tcp_synack_retries= 2
為了打開對端的連接,內核需要發送一個SYN并附帶一個回應前面一個SYN的ACK,也就是所謂三次握手中的第二次握手,這個設定決定了內核放棄連接之前發送SYN+ACK包的數量,
net.ipv4.tcp_syn_retries= 2
在內核放棄建立連接之前發送SYN包的數量,
#net.ipv4.tcp_tw_len= 1net.ipv4.tcp_tw_reuse= 1
開啟重用,允許將TIME-WAITsockets重新用于新的TCP連接,
net.ipv4.tcp_wmem= 8192 436600 873200
TCP寫buffer,可參考的優化值:8192 436600 873200
net.ipv4.tcp_rmem = 32768 436600 873200
TCP讀buffer,可參考的優化值:32768 436600 873200
net.ipv4.tcp_mem= 94500000 91500000 92700000
同樣有3個值,意思是:
net.ipv4.tcp_mem[0]:低于此值,TCP沒有記憶體壓力,net.ipv4.tcp_mem[1]:在此值下,進入記憶體壓力階段,net.ipv4.tcp_mem[2]:高于此值,TCP拒絕分配socket,
上述記憶體單位是頁,而不是位元組,可參考的優化值是:7864321048576 1572864
net.ipv4.tcp_max_orphans= 3276800
系統中最多有多少個TCP套接字不被關聯到任何一個用戶檔案句柄上,
如果超過這個數字,連接將即刻被復位并列印出警告資訊,
這個限制僅僅是為了防止簡單的DoS攻擊,不能過分依靠它或者人為地減小這個值,
更應該增加這個值(如果增加了記憶體之后),
net.ipv4.tcp_fin_timeout= 30
如果套接字由本端要求關閉,這個引數決定了它保持在FIN-WAIT-2狀態的時間,對端可以出錯并永遠不關閉連接,甚至意外當機,預設值是60秒,2.2 內核的通常值是180秒,你可以按這個設定,但要記住的是,即使你的機器是一個輕載的WEB服務器,也有因為大量的死套接字而記憶體溢位的風險,FIN-WAIT-2的危險性比FIN-WAIT-1要小,因為它最多只能吃掉1.5K記憶體,但是它們的生存期長些,
同時還涉及到一個TCP 擁塞演算法的問題,你可以用下面的命令查看本機提供的擁塞演算法控制模塊:
sysctlnet.ipv4.tcp_available_congestion_control
對于幾種演算法的分析,詳情可以參考下:TCP擁塞控制演算法的優缺點、適用環境、性能分析,比如高延時可以試用hybla,中等延時可以試用htcp演算法等,
如果想設定TCP 擁塞演算法為hybla
net.ipv4.tcp_congestion_control=hybla
額外的,對于內核版高于于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并發處理能力會顯著提高,以上配置僅供參考,用于生產環境請根據自己的實際情況調整觀察再調整,
以上就是良許教程網為各位朋友分享的Linux服務器高并發調優實操,
本文由博客一文多發平臺 OpenWrite 發布!
轉載請註明出處,本文鏈接:https://www.uj5u.com/caozuo/252410.html
標籤:其他
下一篇:Shell 腳本編程最佳實踐
