1. 程式人生 > >淺談高併發系統性能調優

淺談高併發系統性能調優

女主宣言

今天帶來的是一個篇長文,主要講解高併發系統架構指標及調優測試經驗,希望能對您的研究有所幫助。本文最先發佈於 OpsDev,轉載已獲取作者授權。

PS:豐富的一線技術、多元化的表現形式,盡在“HULK一線技術雜談”,點關注哦!

640?wx_fmt=jpeg

The Future of Space Exploration

by NASA IOTD

高併發系統的優化一直以來都是一個很重要的問題,下面基於筆者的實踐,和大家聊聊高併發系統的一些調優和優化策略。

1

系統性能的關鍵指標

  • 吞吐量(Throughput) 系統單位時間內處理任務的數量

  • 延遲(Latency) 系統對單個任務的平均響應時間

一般來說,考量一個系統的效能主要看這兩個指標。而這兩個指標之間又存在著一些聯絡:對於指定的系統來說,系統的吞吐量越大,處理的請求越多,伺服器就越繁忙,響應速度就會慢下來;而延遲越低的系統,能夠承載的吞吐量也相應的更高一些。

一方面,我們需要提高系統的吞吐量,以便服務更多的使用者,另一方面我們需要將延遲控制在合理的範圍內,以保證服務質量。

2

系統性能測試

  • 業務場景 

    對於不同的業務系統,可以接受的延遲(Latency)也有所不同,例如郵件服務可以忍受的延遲顯然要比Web服務高得多,所以首先我們需要根據業務場景的不同來定義理想的Latency值。

  • 測試工具 

    我們需要一個能夠製造高吞吐的工具來測試系統的效能,本文中使用的Tsung,它是一個開源的支援分散式的壓力測試工具,它功能豐富,效能強大,配置簡單,並支援多種協議(HTTP、MySQL、LDAP、MQTT、XMPP等)。

  • 測試流程 

    測試的過程中需要不斷加大吞吐量,同時注意觀察服務端的負載,如果負載沒有問題,那就觀察延遲。一般這個過程需要反覆很多次才能測出系統的極限值,而每次測試消耗的時間也比較長,需要耐心一些。

3

通用的系統引數調優

Linux核心預設的引數考慮的是最通用的場景,不能夠滿足高併發系統的需求。

4

伺服器引數調優

編輯檔案/etc/sysctl.conf,新增以下內容:

fs.nr_open = 100000000  

fs.file-max = 100000000

關於這兩項配置的含義可以檢視系統手冊:

640?wx_fmt=jpeg

可以看到file-max的含義:它是系統所有程序一共可以開啟的檔案數量,它是系統級別的,因此應該儘量將它調的大一些,這裡將它修改為一億。 這裡說到需要增大/proc/sys/fs/inode-max的值,但是執行如下命令時發現沒有該配置項:

$ cat /proc/sys/fs/inode-max  

cat: /proc/sys/fs/inode-max: No such file or directory

再看inode-max說明:

640?wx_fmt=jpeg

可以看到有的系統中沒有這個引數,所以先不管了。

對於nr_open,系統手冊裡沒有找到關於它的定義,不過我們可以參考kernel文件:

https://www.kernel.org/doc/Documentation/sysctl/fs.txt

nr_open:      

This denotes the maximum number of file-handles a process can      allocate. Default value is 1024*1024 (1048576) which should be      enough for most machines. Actual limit depends on RLIMIT_NOFILE      resource limit.

可以看到nr_open的描述與file-max十分相近,不仔細看幾乎分辨不出區別。重點在於,file-max是對所有程序(all processes)的限制,而nr_open是對單個程序(a process)的限制。這裡給出了nr_open的預設值:

1024*1024 = 1048576

編輯檔案/etc/security/limits.conf,新增如下內容:

編輯檔案/etc/sysctl.conf,新增以下內容:

*      hard   nofile      4194304  

*      soft   nofile      4194304

關於這兩項配置的含義可以檢視系統手冊:

640?wx_fmt=jpeg

這裡的意思很明確:hard意為硬資源限制:一旦被superuser設定後不能增加;soft為軟資源設定:設定後在程式執行期間可以增加,但不能超過hard的限制。程式讀取的是soft,它是一個告警值。 nofile意為使用者開啟最大檔案描述符數量,在Linux下執行的網路伺服器程式,每個tcp連線都要佔用一個檔案描述符,一旦檔案描述符耗盡,新的連線到來就會返回"Too many open files"這樣的錯誤,為了提高併發數,需要提高這項配置的數值。

這裡有一點需要特別注意,而手冊裡面也沒有細說:在CentOS7下(其他系統還未測試過),nofile的值一定不能高於nr_open,否則使用者ssh登入不了系統,所以操作時務必小心:可以保留一個已登入的root會話,然後換個終端再次嘗試ssh登入,萬一操作失敗,還可以用之前保留的root會話搶救一下。

修改完畢執行:

# sysctl -p

通過以上,我們修改了/etc/security/limits.conf和/etc/sysctl.conf兩個配置檔案,它們的區別在於limits.conf是使用者層面的限制,而sysctl.conf是針對整個系統層面的限制。

5

壓測客戶機引數調優

對於壓測機器來說,為了模擬大量的客戶端,除了需要修改檔案描述符限制外,還需要配置可用埠範圍,可用埠數量決定了單臺壓測機器能夠同時模擬的最大使用者數量。

  • 檔案描述符數量:修改過程同伺服器

  • 可用埠數量:1024以下的埠是作業系統保留的,我們可用的埠範圍是1024-65535,由於每個TCP連線都要用一個埠,這樣單個IP可以模擬的使用者數大概在64000左右 修改/etc/sysctl.conf檔案,新增如下內容:

net.ipv4.ip_local_port_range = 1024 65535

修改完畢執行:

# sysctl -p

需要注意的是,伺服器最好不要這樣做,這是為了避免服務監聽的埠被佔用而無法啟動。如果迫於現實(例如手頭可用的機器實在太少),伺服器必須同時用作壓測機器的話,可以將服務監聽埠新增到ip_local_reserved_ports中。下面舉例說明:

修改/etc/sysctl.conf檔案,新增如下內容:

net.ipv4.ip_local_reserved_ports = 5222, 5269, 5280-5390

修改完畢執行:

# sysctl -p

TCP/IP協議棧從ip_local_port_range中選取埠時,會排除ip_local_reserved_ports中定義的保留埠,因此就不會出現服務埠被佔用而無法啟動的情況。

6

程式調優

對於不同的業務系統,需要有針對性的對其進行調優,本文中測試的目標服務使用Erlang/OTP寫就,Erlang/OTP本身帶有許多的限制,對於一般場景來說這些預設的設定是足夠的;但是為了支援高併發,需要對Erlang虛擬機器進行一些必要的引數調優,具體可以參考官方效能指南

http://erlang.org/doc/efficiency_guide/advanced.html#system-limits

7

服務程式引數調優

程序(process)數量 

Erlang虛擬機器預設的程序數量限制為2^18=262144個,這個值顯然是不夠的,我們可以在erl啟動時新增引數+P來突破這個限制

$ erl +P 10000000

需要注意的是:這樣啟動,erlang虛擬機器的可用程序數量可能會比10000000大,這是因為erlang通常(但不總是)選擇2的N次方的值作為程序數量上限。

原子(atom)數量 

Erlang虛擬機器預設的原子數量上限為1048576,假如每個會話使用一個原子,那麼這個預設值就不夠用了,我們可以在erl啟動時新增引數+t:

$ erl +t 10000000

從另一個角度來說,我們在編寫Erlang程式時,使用原子需要特別小心:因為它消耗記憶體,而且不參與GC,一旦建立就不會被移除掉;一旦超出原子的數量上限,Erlang虛擬機器就會Crash,參見 How to Crash Erlang。

https://prog21.dadgum.com/43.html

埠(port)數量 埠提供了與外部世界通訊的基本機制(這裡的埠與TCP/IP埠的概念不同,需要注意區別),每個Socket連線需要消耗1個埠,官方文件裡面說預設埠上限通常是16384,但根據實測,Linux系統預設為65536,Windows系統預設為8192,無論多少,在這裡都是需要調整的:在erl啟動時新增引數+Q Number,其中Number取值範圍是[1024-134217727]:

 $ erl +Q 10000000

8

壓測指令碼調優

壓測工具使用Tsung。Tsung支援多種協議,有著豐富的功能,並且配置簡單靈活。下面是幾點需要注意的地方:

記憶體

對於單個Socket連線來說消耗記憶體不多,但是幾萬甚至幾十萬個連線疊加起來就非常可觀了,配置不當會導致壓測端記憶體成為瓶頸。

  • TCP 傳送、接收快取 Tsung預設的TCP/UDP快取大小為32KB,本例中我們測試的服務採用MQTT協議,它是一種非常輕量級的協議,32KB還是顯得過大了,我們將它設定為4KB大小就足夠了:

<option name="tcp_snd_buffer" value="4096"></option>  

<option name="tcp_rcv_buffer" value="4096"></option>

  • Tsung能夠讓模擬使用者的程序在空閒時間(thinktime)進入休眠,用以降低記憶體消耗,預設空閒10秒觸發,我們可以將它降低到5秒:

<option name="hibernate" value="5"></option>

IO

  • 不要啟用dumptraffic,除非用於除錯,因為它需要將客戶機和伺服器往來的協議寫入磁碟日誌中,是一個IO開銷非常大的行為。筆者曾經遇到過一次這樣的問題,測試部門的同事使用JMeter,壓測過程中,服務端一直處於較低的負載,但JMeter最終得出的壓測報告卻包含很多超時錯誤。經過仔細排查,才定位到原來是壓測端預設開啟了debug日誌,海量的debug日誌生生拖垮了壓測機器。所以遇到這種情況時,可以先檢查一下壓測端配置是否正確。

<tsung loglevel="error" dumptraffic="false" version="1.0">

日誌等級要調高一些,日誌等級過低會列印很多無用資訊:一方面會加大IO開銷,另一方面會讓有用的ERROR資訊淹沒在海量的除錯日誌中。

如果Tsung從CSV檔案讀取使用者名稱密碼,那麼該CSV檔案不能過大,否則讀取該CSV將會變成一個極其耗時的操作,特別是當壓測程式需要每秒產生大量使用者時。

網路

有時候為了避免網路擁塞,需要限制壓測客戶機的頻寬,使流量以比較平滑的速率傳送和接收。

<option name="rate_limit" value="1024"></option>

其採用令牌桶演算法(token bucket),單位KB/s,目前只對流入流量有效。

9

定位系統性能瓶頸

當系統吞吐和延遲上不去時,首先需要定位問題,而不是急於修改程式碼。

常見的效能瓶頸包括CPU/記憶體/磁碟IO/網路頻寬等,其中每一項都有一到多個簡單實用的工具: 對於CPU和記憶體,我們只要使用top就可以了;對於磁碟IO,可以用iotop或iostat;對於網路頻寬,可以使用iftop。

如果依然沒能定位到問題,可能系統配置不當,參考通用的系統引數調優。

最後檢查程式碼是否有單點瓶頸,例如程式被阻塞了:在筆者實測過程中,發現每個使用者建立會話程序都需要對同一個supervisor發起同步請求,同時登入的使用者數量很大時,這些同步請求會排隊,甚至引發超時。

10

結束語

以上就是筆者做壓力測試時遇到的一些問題以及應對辦法,鑑於筆者水平有限,錯漏難免。拋磚引玉,歡迎交流指正。

HULK一線技術雜談

由360雲平臺團隊打造的技術分享公眾號,內容涉及雲端計算資料庫大資料監控泛前端自動化測試等眾多技術領域,通過夯實的技術積累和豐富的一線實戰經驗,為你帶來最有料的技術分享

640?wx_fmt=gif