最近工作上的兩個問題的調查報告

上週收到分公司同事反映tribonsem速度很慢,影響工作效率。領導派遣xx,xx還有我前去調查情況。我們在二號線並沒有找到sem速度慢的實例,只找到了兩個tribon的問題。經過我們採集數據並進行分析,初步給出的結論如下。

一:tribon Show Surrounding Models功能緩慢

問題描述:

經過測試,載入模型、旋轉和渲染功能都很流暢,只有在使用調取模型周圍模型的功能時,會出現較慢的反映。

 

  1. 問題初步分析:

同樣的軟件在不同環境下有不同的性能表現,比較直接的思路就是去辨別究竟是哪個環境上的差別導致了問題的出現。最容易想到的環境區別就是網絡差異,***和局域網在吞吐量(帶寬)和時延差異巨大。第一我們需要找到問題究竟是帶寬瓶頸還是時延瓶頸。如果是帶寬原因那麼增加帶寬就可以解決問題,如果是時延原因就比較麻煩,因爲***的時延是天然存在的,只能通過軟件本身來減小時延的影響。第一反應傾向於延時,因爲***的帶寬是共享的,使用人數不同的時候性能差異會很大,既然用戶並沒有反映出現性能抖動的現象,帶寬瓶頸的可能性並不高。

 

數據採集和分析過程:

速度慢只是一種心理反映,是否真的慢,慢多少需要實際的數據證據,同時我們也需要數據來證明自己的猜測。

我們在本部和分公司分別截取了問題出現時的網絡數據,通過比較兩組數據的差異,我們就可以找到問題發生的原因。

 

這兩組數據的統計摘要如下:

 

分公司

 wKioL1OhLxORDl7IAAWiBTWXyYE850.jpg


 

 

 本部:

wKiom1OhMWfSsX1wAAWVt7c9M84427.jpg

 


 

數據說明:

這兩組數據的關鍵部分是最下方的display欄中的displayed部分,這部分是tribon的客戶端同服務器端的交互數據統計。我們連接的是同一臺服務器,也就是ip地址爲172.16.101.33t1309服務器,不過兩次調用的模型不同。因爲症狀是模型無關的,所以我們忽略這個影響。我們可以看到最關鍵的差別在於數據傳輸的速度。二號線的速度是1.295Mbit/s,而外高橋能達到5.484Mbit/s。本部的速度是分部的4.23倍。最直接的猜測是由於帶寬瓶頸。帶寬瓶頸所表現出的特徵是瓶頸處的流量始終處於峯值狀態,才能保證任何時刻都網絡訪問速度都緩慢。同時帶寬瓶頸會表現出不分應用的網絡丟包。因爲在沒有qos(優先級)情況下的網絡接口在出現接近帶寬瓶頸的時候會對數據包採取隨機丟棄的策略。不會出現有的功能正常,其他功能異常的情況。

 

本部至分部的接口數據如下:

 

Peak value of input: 7246733 bytes/sec, at2014-05-28 08:50:21

Peak value of output: 19571139 bytes/sec,at 2014-05-09 07:03:32

Last 300 seconds input:  1264 packets/sec 355256 bytes/sec 0%

Last 300 seconds output:  1655 packets/sec 1252578 bytes/sec 1%

 

可以很清楚的看見,在當前察看網絡狀態的時候,網絡流量和峯值流量差了一個數量級。

既然如此,那麼這個差異究竟是怎麼產生的,我們繼續察看具體的數據。

 

分部的數據部分截圖:


  wKiom1OhMaHBP4FXAAmP2jCszps444.jpg

 

本部部分的數據截圖:


 wKioL1OhMbWjBwWwAAsff0AFblw126.jpg

 

數據說明:

我們看到這兩段數據最明顯的差異在相鄰的ip方向相反兩個報文的時間差,也就是傳輸時延。

這裏需要注意我的說法,因爲相鄰的ip方向一致的時間差並不表示傳輸延時,而表示的發送延時。發送延時的上限取決於客戶端應用程序想要發送的數據量,以及客戶端電腦的網卡工作頻率。這個影響目測就可以看出差距來並不大。

而最關鍵之處在於網絡兩端一次交互的時間差,專業的說法就是RTT(round trip time-報文往返時間)。在相同數據量的情況下,網絡兩端相互交互的次數越少,數據的傳輸速度越快。減少數據交互次數有兩個辦法,一是增加每個報文的大小,二是一次批量發送多個報文。這兩種方法都可以有效的增加網絡速度。

根據這個思路,我們再來看下一組數據對比。兩地RTT區別:

分部:


wKiom1OhMhLRX9YcAAGG30YlaOE369.jpg 

本部:


 wKioL1OhMg-jctyMAAIJ6hXSFDU025.jpg

 

需要注意的是兩組數據的單位是不同的。

同時這部分包括了服務器計算上的時延,因此rtt=網絡傳輸時延+計算時延

按照統計學的相關理論,E(x+y)=E(x)+E(y),即兩個隨機變量的和的期望具有線性性質。

因爲我們採集數據的時間段是類似的,因此我們可以假設兩次統計計算時延的數學期望(即統計的平均值)是相等的。因此網絡延時的期望的差值就等於兩次統計的rtt的差值。

在圖中可以看出本部的RTT數據集中在2ms以下區間,而分部則高得多,集中在10ms以下。在假設分佈均勻的情況下,平均值的差距在4ms左右。比較符合網絡實際性能差別的預期。

另外我們可以看到無論是分部還是在本部超高的RTT值是非常零星的,無可以得到的結論是網絡的穩定性較好,極少出現丟包的情況。另外需要說明的是,網絡上確認是否丟包的依據是和RTT有關的參數RTO,而RTO的協議最小值爲200ms,顯然和上圖中的數據不符。

 

因此憑藉對圖形的目測就可以可以得出結論,即對於該應用來說網絡性能是正常的。

 

既然網絡並無異常,那我們繼續去挖掘網絡性能的差異是如何造成應用的性能差異。我們再看一下兩組數據的數據包長度分佈統計。

 

分部:


wKiom1OhMmPxAS9fAAJhcL_Uo1k743.jpg 

本部:

wKioL1OhMmGz0glqAAJeKrX4sKM311.jpg

從這兩份統計數據來看,兩次數據統計都呈現出一個相似的特徵,即長度在80-159字節的數據佔數據包占全部統計的一半左右。我再解釋一下這個數據的意義。

對於一個標準的以太網網絡,標準數據包的最小單位是64bit,而以太網幀頭佔18字節,ip協議頭佔20字節,tcp協議頭佔20字節。也就是說在沒有任何數據需要傳輸的情況下,就需要消耗掉48個字節的數據。對於一個160字節的數據包,有效數據容量在70%左右。而標準的以太網幀的最大長度可達到1518字節。可見,這個數據包的大小是非常小的。另外,對於通常的IO而言,每次傳輸數據的時間=時延+單次傳輸數據量/帶寬,並且傳輸的總時間=傳輸次數*單次傳輸時間,傳輸次數=總數據量/單次傳輸數據量,把公式整合,可以得到傳輸總時間=數據總量*時延/單次傳輸數據量+數據總量/帶寬,時延和帶寬是被硬件決定的,所以可以看成一個常量,而數據總量是固定的,因此爲了減少總的傳輸數據時間,需要增加每次傳輸的數據量。由此可以知小數據包在IO上有天然的弱勢。

同時,還有一個因素也產生了一定的影響,因爲每次數據傳輸都會伴隨着一次應用程序從用戶態至內核態的轉換,這也是需要花費一定的時間的。

但是問題的關鍵不僅僅在於有效容量的降低,關鍵之處在於上文說的網絡數據的傳輸模式不同於一般的IO傳輸。

這個數據的傳輸模式是傳輸一組數據,等待確認後再傳另一組數據。這樣,會話次數會隨着報文的減小以rtt爲係數線性增長。一般較爲優化的網絡傳輸通常採用流水線的方式傳遞數據。能夠更有效的利用帶寬,而這個程序沒能做到這點。

根據推斷這極有可能是性能差異的主要原因。

 

數據包非常小的原因有兩種情況,一是數據本身比較小,二是數據比較緊急,需要立刻發出去。網絡編程接口的默認值是第一種情況,但是程序員可以通過附加參數的方式來進行緊急推送,即第二種情況。

對於默認的情況,網絡系統會採用Nagle算法來協調網絡時延和吞吐量之間的關係。

這個算法很好理解。比如很多人排隊一起坐電梯,最優吞吐量的辦法是等人上滿電梯才運行。但是這樣所付出的代價是已經在電梯裏面的人需要等外面的人才能走,會增大延時。而最低時延的辦法是電梯上來一個人立刻就走,但是因爲每次只能載一個人,所以整體的吞吐量就會下降。Nagle算法就是一個犧牲單個數據包時延的方式來獲取更大吞吐量的方法。

我們所需要確認的是導致問題產生的原因是否是由於落入了第二種情況。

因爲如果是默認情況,我們只需要修改操作系統的tcp協議中Nagle算法的參數就可以進行調整,而如果陷入了第二種情況,則需要在程序中進行調整。

從網絡角度,我們可以通過數據包tcp協議頭的flag字段的push位來辨別是否該數據是強制推送的。

因此我們有了下一步的思路,去尋找psh1200字節以下的小數據包,觀察裏面究竟承載了什麼樣的數據。

 

因爲兩邊的數據模式極爲相似,因此我只截取分部的部分,如下圖:

wKioL1OhMo7iOO8LAAs8Lguf1rY092.jpg

 

 

對這部分數據進行統計的結果如下:

 


wKiom1OhMumCb9FPAAUiSLUZK50647.jpg 

把上述統計同最初的統計作對比,我們可以發現以下問題:

  1. 滿足條件的數據包超過整體包數的60%以上。

  2. 傳輸的時間貫穿整個會話。

  3. 小包數據總量佔整體數據的量非常小。因此可以確定主要數據部分是以大包進行發送的。但。但是小包的數量非常多,因此拖累了整體的網絡性能。

 

經觀察,小數據包的數據模式非常固定。可以肯定的是,這是一些固定的數據結構。猜測包含了一些模型的元數據,以及一些固定的消息。

例如下面的數據樣本:

0000  44 37 e6 5a 32 ed 50 3d e5 a8 78 3f 08 00 45 00  D7.Z2.P=..x?..E.

0010  00 a0 09 46 40 00 7d 06 c4 6f ac 10 65 21 0a 16  ...F@.}..o..e!..

0020  14 5b 02 99 03 3f 91 da 22 93 19 35 54 d0 50 18  .[...?.."..5T.P.

0030  f5 c8 e3 e1 00 00 80 00 00 74 34 1d f7 81 00 00  .........t4.....

0040   00 01 0000 00 00 00 00 00 00 00 00 00 00 00 00 ................

0050   00 00 0000 00 50 00 00 00 50 31 33 30 39 3d 54 .....P...P1309=T

0060   42 31 3333 3d 44 4d 32 30 20 20 20 20 20 20 20 B133=DM20      

0070   20 20 2020 00 00 d4 b5 00 00 5e a0 d3 52 91 00     ......^..R..

0080   00 00 0000 c2 01 01 00 06 00 00 00 00 00 00 00 ................

0090   00 00 0000 00 00 01 00 00 00 00 00 00 00 00 00 ................

00a0   00 00 0000 00 00 00 00 00 00 00 00 00 00       ..............

 

灰色部分爲實際的用戶數據,採用ascii解碼可以看到有p1309=TB133=DM20字樣,之前和後面的數據應該是填出數據和其他的數據類型。

 

結合上述證據,我推斷第一個問題產生是由於tribon本身應用程序在該功能上對於慢速網絡優化不足導致的。如果推斷屬實,那麼最佳的辦法是把服務器遷移到本地。至於起到緩存作用的網絡加速設備是否能夠降低延時則取決於應用程序的交互模式是否允許這樣做,可以試一下。

二.切割指令速度緩慢

問題描述:

在執行切割指令操作的時候,會等待非常久的時間纔會有反應。

 

初步診斷:

這個問題的診斷思路和上一個問題比較相近,通過比較捕獲來數據包來確定問題出現的範圍。但是在截獲數據包的過程中發生了奇怪的現象,命令還沒有執行完畢,剛開始卡在沙漏狀態的時候,到目標1309目標服務器的網絡已經沒有數據了。因此,問題範圍從網絡轉移到了本地計算機。

於是,我拿出了procmon來對該進程進行監控,發現了這時候改進成正在向tech9寫入數據。

如圖:

 

 wKiom1OhMxDRGU34AAN0ONp7Uho550.jpg

 


 

 

統計信息如下:

 

wKioL1OhMwPg8Pc7AAIs-azeEb0807.jpg

 

我們在本部也截取了同一個操作的數據,如下圖:


 

 wKiom1OhM2GTVeHxAAHWIDogeOQ602.jpg

這兩組數據裏面,可以得到對於這個gen文件的寫入,分部的速度爲1758 byte/s,而本部的速度爲12099 byte/s。本部的速度爲分部的6倍。但是實際上,對於一個寫入文件這種操作來說,本部的速度也非常低。

需要注意的是這兩個操作所截取的寫入事件數量都是非常之高的,並且每個事件的數據量很小。

 

這樣,就可能有兩個原因:

  1. tech9IO可能有問題。

  2. 程序的寫入模式可能存在問題。

 

驗證方法比較簡單,我寫了一個測試程序用來測試tech9寫入是否存在異常。

 

測試代碼如下:

#testwrite.py

 

from random import choice

from string import letters

 

def test():

           writetimes=10000

           testfile=r'z:\testfile'

           f=open(testfile,'w')

           fori in range(writetimes):

                     f.write(choice(letters))

           f.close()

 

if __name__=='__main__':

           test()

 

我把tech9映射成本地磁盤,分1000次向testfile中寫入隨機字母的數據。

 

Procmon監測的結果如下:

wKioL1OhM2mQXiZ3AAEcAIlDncQ092.jpg


wKiom1OhM7fTBBEcAACedJQsKcI486.jpg 

很顯然,對於一個標準的文件寫入程序來說,tech9的表現是完全正常的。現在我想辦法模擬tribon寫入文件時的系統表現,就是寫入能截獲的系統event數量非常高。這樣,從文件寫入原理上來講,程序會先把要寫入的文件放在內存緩衝區裏面,再把緩衝區的數據同步到硬盤上面。

每次內存和硬盤交互,可以由flush操作觸發。因此我們不斷地flush緩衝區,看看系統的表現如何。

測試代碼如下:

from random import choice

from string import letters

 

def test():

           writetimes=10000

           testfile=r'z:\testfile'

           f=open(testfile,'w')

           fori in range(writetimes):

                     f.write(choice(letters))

                     f.flush()

           f.close()

 

if __name__=='__main__':

           test()

 

我在每次寫入的時候,調用了flush操作,把緩衝區寫入到硬盤。

Procmon的監控結果如下:

wKioL1OhM7iBlQtIAAOlaExVHq8669.jpg

性能統計如下:

wKiom1OhNBLQtMvvAAG5vtRN1io616.jpg

 

可以看到,同樣的文件,因爲不同的寫入方式,前者的時間是0.0079s,而後者是到30.988s。這個原理和上一個問題的原理類似。

 

並且這個測試程序從系統事件上完全再現了tribon的表現。

 

因此,可以由此猜測,第二個問題產生的原因極有可能是由於tribon在寫入文件時過度地進行同步操作。並非是tech9io問題引起的,最早我的猜測過度草率了。

 

由此看來,即使切換文件服務器,可能也是無濟於事的。

 

 


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章