一、利用Fiddler模擬惡劣網絡環境
在解決日常的支持需求中,經常會遇到一些用戶反饋一些無法簡單復現的bug,有很大一部分的bug是由於用戶自身的網絡環境波動,或者是本身網絡環境就較為惡劣,而服務在面對這種惡劣的網絡環境的健壯性不夠,導致會出現一些意想不到的bug。而在正常的開發自測過程中很難去營造出這種惡劣的網絡環境,使得這些bug較難被提前發現和修復。另外一些服務在惡劣網絡環境下雖然不會出現不可用的情況,但是用戶體檢很差,為了優化這個情況下的用戶體驗,也需要去在本地模擬這種環境來進行調優。
所以要去復現這些bug,甚至是去提前發現這些bug,就需要能夠在開發環境中模擬出惡劣的網絡環境,從而看到在這種惡劣的網絡環境下的服務的表現等。當前模擬惡劣網絡環境主要可以通過以下這些手段實現:
- 通過應用層或者傳輸層的代理服務器,通過在代理服務器上設置一些模擬惡劣網絡環境的參數,使得通過這些代理服務器的流量都被轉化為惡劣網絡環境下的流量。如利用Fiddler,Charles等具有代理服務器功能的網絡流量分析軟件來實現。
- 通過利用一些更底層的驅動層面的服務,通過控制網卡的收包發包的行為,來模擬惡劣的網絡環境。如dummynet的ipfw驅動等。
- 通過建立一個可控的網關,在網關上部署模擬惡劣環境的相關程序,所有需要借助該網關進行轉發的流量都會被模擬為惡劣網絡條件。Linux下的netem就提供了這類支持。
這里主要先講的是第一種手段,即利用Fiddler來模擬惡劣的網絡環境,對服務進行測試,這個手段實現簡單,較為直觀,但是缺點是只能支持那些利用HTTP進行通信和交互的服務。在之后的文章中也會進一步說一下后兩種手段。
【Fiddler是啥】
Fiddler的官網上是這樣描述它自己的:The free web debugging proxy for any browser, system or platform,即跨瀏覽器、跨系統、跨平台的免費Web Debug代理服務器。當你的HTTP瀏覽經過Fiddler時,Fiddler可以監視流量,查看HTTP通訊的各種信息,設置斷點查看和修改HTTP數據,甚至可以構造各種測試用的HTTP包以及重放已記錄的包等。其官網是http://www.fiddler2.com/fiddler2/,上面詳細地介紹了Fiddler到底是什么。
【簡單地利用Fiddler限速模擬惡劣網絡環境】
Fiddler本身已經預置提供了模擬Modem速度的選項,其位置位於:
Rules – Performances – Simulate Modem Speeds
勾選該選項后,所有通過Fiddler代理的流量都會變得和多年前的56k小貓時上網一般的慢。
由於Fiddler只是一個HTTP代理,要直觀地看出限速效果,最好是運行在瀏覽器中的測速工具,這里選用speedtest.net提供的測速工具進行測試。
首先是開啟該選項之前的速度:
打開了Simulate Modem Speeds后:
速度已經回到了當年那種無法忍受的低速了,注意到這里PING值也有了顯著的提高,而事實上ping值是ICMP層的控制報文,並不會被Fiddler影響,理論上ping值並不會出現提高的情況,進一步分析Fiddler中的報文則可以看出端倪:
事實上網頁插件並不能實現發送ICMP包並得到ping值的功能,而是用多次較小的HTTP GET請求的響應時間來計算PING值,這里實際算出來的是一個平均的HTTP的RTT值,所以受到Fiddler模擬惡劣環境的影響就是正常的了。
【調整模擬惡劣網絡環境的參數】
直接模擬Modem速度實在是慢爆了,事實上就算是在很差信號的情況下,手機移動網絡的速度都已經超過了當年的56k Modem速度了,所以采用默認的配置模擬出來的環境過於惡劣,並不一定符合需求,此時就需要對限速的參數進行調整。
Fiddler本身就提供了一個配置文件供調整這些參數,點擊:
Rules – Customize Rules…
就會用文本編輯器打開CustomRules.js文件,其默認位於用戶目錄的文檔目錄下的\Fiddler2\Scripts 位置,后綴名是js,其內容實質是JScript.NET——微軟對ECMAScript規范的實現,與日常使用的javascript是屬於同一個規范下的,但是在擴展的細節實現存在一定的不同。
打開該文件后,可以找到一個m_SimulateModem標志位:
if (m_SimulateModem) { // Delay sends by 300ms per KB uploaded. oSession["request-trickle-delay"] = "300"; // Delay receives by 150ms per KB downloaded. oSession["response-trickle-delay"] = "150" }
- 該標志位控制着oSession的兩個參數值的設置,當勾選了Simulate Modem Speeds時,request-trickle-delay與response-trickle-delay就會被設置,其中request-trickle-delay中的值代表每KB的數據被上傳時會被延時多少毫秒,response-trickle-delay則對應下載時每KB的數據會被延時多少毫秒,如果本身網速已經相當快的話,這里設置的值就可以近似地推算出開啟模擬后的上傳和下載帶寬了,比如默認設置下下載延時為150ms,上傳延時為300ms,對應可以推算出大致的模擬帶寬為:
上傳帶寬=(1*8/1000)/0.300≈0.053Mbps
下載帶寬=(1*8/1000)/0.150≈0.027Mbps
然而實際情況下卻得到了兩倍於這個值的帶寬,推測可能是Fiddler的內部實現上有一些和描述上的不同,為何為造成這個現象現在還不是很清楚,所以上述公式最后還需要修正一個2.0的系數,即:
上傳帶寬=((1*8/1000)/0.300)*2.0≈0.106Mbps
下載帶寬=((1*8/1000)/0.150)*2.0≈0.053Mbps
假設我們將兩個參數都設置為50,則會得到上下載帶寬均為0.32Mbps,測速結果如下所示:
【編寫自定義腳本】
進一步地,我們可以擴展CustomRules.js里的邏輯,參照Jscript的文檔可以在模擬惡劣環境中加入更多自定義的邏輯,這里實現了一個隨機延時量設置,使得網絡帶寬不是恆定為一個低速的值,而是會在一定范圍內隨機抖動:
static function randInt(min, max) { return Math.round(Math.random()*(max-min)+min); } if (m_SimulateModem) { // Delay sends by 300ms per KB uploaded. oSession["request-trickle-delay"] = ""+randInt(1,50); // Delay receives by 150ms per KB downloaded. oSession["response-trickle-delay"] = ""+randInt(1,50); }
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
得到的測試結果如下:
在測速過程中的瞬時速度的趨勢圖如下:
可以看到整體的網絡限速存在了一定程度的抖動。
通過進一步擴展CustionRules.js可以實現很多需要的惡劣環境模擬場景,如果場景較為復雜的話,也可以通過編寫Fiddler的插件的方式,編寫C#插件代碼來進一步控制Fiddler的行為,在這里就不多做贅述了。詳細可以參照:http://docs.telerik.com/fiddler/extend-fiddler/extendwithdotnet
【Fiddler模擬惡劣網絡環境的局限性】
Fiddler進行限速較為簡單和靈活,配置也較為方便,但是由於它是一個應用層的HTTP的代理,只能模擬該層上的行為,對於一些復雜的網絡層的丟包、重傳等惡劣情況就不能很好的模擬出來,而且對於其他協議的應用也不支持,后續會介紹一些其他的模擬惡劣環境的方法和軟件來彌補這些缺失。
二、利用Dummynet模擬惡劣網絡環境
利用Dummynet模擬惡劣網絡環境
在之前的文章中提到了三種模擬惡劣網絡環境調試代碼的手段:
- 應用層或者傳輸層的代理服務器
- 傳輸層或者網絡層控制數據包的驅動
- 網絡層控制數據包的網關
同時在之前的文章中介紹了第一種手段,即利用應用層的HTTP代理Fiddler來模擬惡劣網絡環境,這種方式簡單且靈活,但是其處於應用層,限制較大,同時也沒有辦法從帶寬和延時兩個方面分別去精細化地對惡劣網絡環境進行模擬,這里介紹第二種手段——Dummynet。
Dummynet簡介
Dummynet的官網地址是:http://info.iet.unipi.it/~luigi/dummynet/,官網上對於dummynet的描述是這樣的:dummynet is a live network emulation tool, originally designed for testing networking protocols, and since then used for a variety of applications including bandwidth management. 即Dummynet是一個實時的網絡模擬工具,事實上dummynet是ipfw防火牆的一部分,ipfw是一個網絡層的防火牆,並內建於FreeBSD之中。
利用ipfw的options DUMMYNET選項,可以設置一系列的pipes,從而做到對網絡流量進行控制的目的。
Dummynet基本原理
如官網上提供的這張圖所示,dummynet通過在兩個網絡層中(一般是在傳輸層和應用層之間或者網絡層與傳輸層之間)建立一條條pipe的方式來控制網絡流量,符合設定的規則中的網絡流量會被引入這些管道中去,從而使得dummynet可以介入這些流量之中,控制帶寬、延遲,甚至進一步控制丟包率等眾多參數。
Dummynet安裝(Windows)
Dummynet本身作為ipfw防火牆的一部分,其內建於FreeBSD,其本身支持FreeBSD,OSX,Linux,Windows多種操作系統下的安裝和使用,在OSX和Linux下下載源碼編譯安裝一般即可使用,這里主要介紹一下Windows下的Dummynet安裝。
Dummynet在Windows下是作為一個網卡上的服務驅動存在的,在官方給出的二進制文件包中已經包含了該驅動的sys與inf文件,但是該驅動是沒有包含數字簽名的,在並沒有引入驅動強制簽名機制的Windows操作系統(如Windows XP)上時,直接到官網下載到最新的二進制包,然后依照之后的操作步驟進行安裝即可以完成ipfw+dummynet服務驅動的安裝,但是在引入了驅動強制簽名機制(一般是Windows 7以后的Windows版本,或者設置了比較嚴格的組策略)的操作系統上,是無法安裝ipfw+dummynet服務驅動的,要解決這個問題可以使用兩種方案:
- 打開Windows的測試模式,關閉驅動強制簽名機制,然后安裝無簽名的驅動
這種方式的實現也有兩種方式,一種是在啟動Windows時啟動選項中關閉驅動強制簽名校驗機制,可以參考這篇文章 http://jingyan.baidu.com/article/7c6fb42879543380642c9036.html ,另外一種方式則是進入Windows的測試模式,可以參考 http://jingyan.baidu.com/article/acf728fd21c3e7f8e510a3ef.html 文章中的說明。 - 為無簽名的ipfw+dummynet服務驅動打上自簽名或者可用的簽名
如何進行自簽名或者打上可用的簽名可以參考MSDN上的說明 https://msdn.microsoft.com/en-us/library/windows/hardware/ff544865(v=vs.85).aspx ,具體如何簽名並不在本文的范疇內。
本文主要使用第二種方式,為ipfw+dummynet服務驅動打上百度簽名后,即可在Windows 7上正常安裝。
安裝的方式如下:
- 找到你需要限制帶寬和流量的網卡,進入屬性:
- 點擊下方的安裝,選擇服務,然后選擇從磁盤安裝,並在瀏覽中找到ipfw+dummynet服務驅動的inf安裝文件:
- 在“此連接使用下列項目”中看到ipfw+dummynet之后,即代表ipfw+dummynet服務驅動安裝成功,如果安裝成功后遇到網卡無法使用的情況,一般會是前一次卸載沒有卸載完全,重啟電腦可以解決。
安裝完成后可以以管理員權限運行binary目錄下的testme.bat,如果運行結果中沒有報警信息或者錯誤信息,則驅動安裝是成功的:
利用Dummynet模擬惡劣網絡環境
利用dummynet的pipe,可以設置一些特定的規則,就可以達到模擬惡劣網絡環境的目的,且這些設置對於在操作系統中運行的應用程序來說是透明的、自動生效的,不需要像在使用Fiddler來進行模擬時還需要設置http代理。
- 低帶寬模擬
惡劣網絡環境最基礎的模擬方式就是模擬較小帶寬下的情況,利用dummynet中的pipe設置,可以將所有的tcp流和udp流的帶寬限制在一定的級別上,運行一個管理員的命令行,並利用binary目錄下的ipfw.exe文件就可以設置dummynet中的pipe,在命令行中執行以下命令:ipfw add pipe 2 in proto tcp
ipfw pipe 2 config bw 2Mbit/s
即可將tcp的下行流量限制在2Mbit/s的帶寬上,這里采用和上一篇文章中采用的相同的測速網站speedtest.net來驗證限制帶寬的效果,在執行以下指令以前的測速結果如下:
在執行完上述指令以后的測速結果如下: - 高延時模擬
在管理員權限的命令行中執行以下命令:ipfw add pipe 10 ip from any to any
ipfw pipe 10 config delay 200
這會使得所有的包都被延時200ms,這里利用ping命令來ping百度的方式來直接測試延時,命令執行以前結果如下:
命令執行以后結果如下: - 高丟包率模擬
在管理員權限的命令行中執行以下命令:ipfw add pipe 10 ip from any to any
ipfw pipe 10 config plr 0.1
這里設置的是10%的丟包率,這里利用ping -t指令繼續ping百度,來觀察丟包率的變化:
執行以前:
執行以后: - 更加精細化的設置
可以看到相比Fiddler來說,利用dummynet可以更加精細化地從各個角度去設置一個惡劣網絡環境的參數,上文中提到的帶寬、延時和丟包率的模擬當然也可以組合起來使用,同時在設置pipe規則時還可以進一步設置更加精細的規則,比如指定的ip段,指定的協議等,比如這一段指令:ipfw add pipe 4 src-ip 10.1.2.0/24 in
ipfw pipe 4 config bw 1Mbit/s delay 123 plr 0.1
就設置了一個只會介入源ip來自10.1.2.0/24網段的數據包的pipe,並且這個pipe會限制1Mbit/s的帶寬,延時123ms,並且有10%的丟包率,通過組合這些參數,可以模擬出很復雜的惡劣網絡環境,從而滿足開發和測試的需要。相關的設置參數的說明和設置方法可以參考官網的相關文檔和其他的一些說明文檔。
在最后,當完成模擬惡劣網絡環境后,執行以下命令: ipfw -q flush
ipfw -q pipe flush
來清除所有設定的pipe和規則,進一步如果需要還原原來的環境的話可以將驅動刪除,並重啟計算機即可。
三、clumsy 0.2模擬網速
clumsy 能在 Windows 平台下人工造成不穩定的網絡狀況,方便你調試應用程序在極端網絡狀況下的表現。
簡介
利用封裝 Winodws Filtering Platform 的WinDivert 庫, clumsy 能實時的將系統接收和發出的網絡數據包攔截下來,人工的造成延遲,掉包和篡改操作后再進行發送。無論你是要重現網絡異常造成的程序錯誤,還是評估你的應用程序在不良網絡狀況下的表現,clumsy 都能讓你在不需要額外添加代碼的情況下,在系統層次幫你達到想要的效果:
特色:
- 下載即用,不需要安裝任何東西。
- 不需要額外設置,不需要修改你的程序的代碼。
- 系統級別的網絡控制,可以適用於命令行,圖形界面等任何 Windows 應用程序。
- 不僅僅只支持 HTTP,任何 TCP, UDP 的網絡連接都可以被處理。
- 支持本地調試(服務器和客戶端都在 localhost)
- "熱插拔",你的程序可以一直運行,而 clumsy 可以隨時開啟和關閉。
- 實時調節各種參數,詳細控制網絡情況。
實例
下面的動畫展示了 clumsy 作用於一個本地的基於 netcat 的 UDP 服務器/客戶端的情況。仔細觀察你可以看到數據根據在 clumsy 的影響下產生了相應的變化。 如果你基本知道了 clumsy 是干什么用的,不妨到下載頁面選擇適用於你系統的版本進行下載。
詳細信息
clumsy 首先根據用戶選擇的 filter 來攔截指定的網絡數據。在 filter 中可以設定你感興趣的協議(tcp/udp),端口號,是接收還是發出的端口。你也可以通過簡單的邏輯語句來進一步縮小范圍。當 clumsy 被激活時,只有符合這些標准的網絡數據會被進行處理,而你不感興趣的數據仍然會由系統正常傳輸。
當被 filter 的網絡數據包被攔截后,你可以選擇 clumsy 提供的功能來有目的性的調整網絡情況:
- 延遲(Lag),把數據包緩存一段時間后再發出,這樣能夠模擬網絡延遲的狀況。
- 掉包(Drop),隨機丟棄一些數據。
- 節流(Throttle),把一小段時間內的數據攔截下來后再在之后的同一時間一同發出去。
- 重發(Duplicate),隨機復制一些數據並與其本身一同發送。
- 亂序(Out of order),打亂數據包發送的順序。
- 篡改(Tamper),隨機修改小部分的包裹內容。
盡管當前寬帶網絡連接十分普及,但網絡傳輸其本身在本質上總不是穩定的。如果你的應用程序中沒有應對各種情況的處理,那么有可能一個丟失的 UDP 包裹都會讓你的程序崩潰。正確的調試這類行為 顯然需要再代碼結構上進行仔細的設計和處理,還會很花功夫。而且在某些封裝緊密的開發環境(Unity3D 自帶的網絡庫可能是一個例子)下會更麻煩。clumsy 以盡可能減輕程序員負擔為目標, 希望提供一個簡單方便(但並不完美)的解決方案。
項目的代碼可以在github上獲取。在下載頁面有編譯好的版本。強烈建議在使用前花點時間閱讀一下文檔,來 了解 clumsy 的功能和限制。