Dragonfly P2P 傳輸協(xié)議優(yōu)化代碼解析
優(yōu)化背景
此前 Dragonfly 的 P2P 下載采用靜態(tài)限流策略,相關(guān)配置項在 dfget.yaml 配置文件中:
# 下載服務(wù)選項。 download: # 總下載限速。 totalRateLimit: 1024Mi # 單個任務(wù)下載限速。 perPeerRateLimit: 512Mi
其中 perPeerRateLimit 為單個任務(wù)設(shè)置流量上限, totalRateLimit 為單個節(jié)點的所有任務(wù)設(shè)置流量上限。
靜態(tài)限流策略的理想情況是: perPeerRateLimit 設(shè)置為20M , totalRateLimit 設(shè)置為 100M ,且該節(jié)點目前運行了 5 個或更多的 P2P 下載任務(wù),這種情況下可以確保所有任務(wù)總帶寬不會超過 100M ,且?guī)挄挥行Ю谩?/p>

這種限流策略的缺點是:若perPeerRateLimit 設(shè)置為 20M , totalRateLimit 設(shè)置為 100M ,并且當前該節(jié)點只運行了一個下載任務(wù),那么該任務(wù)的最大下載速度為 20M ,和最大帶寬 100M 相比,浪費了 80% 的帶寬。

因此,為了最大限度地利用帶寬,需要使用動態(tài)限流來確保任務(wù)數(shù)量少時能能充分利用總帶寬,而任務(wù)數(shù)量多時也能公平分配帶寬。最終,我們設(shè)計出一套根據(jù)上下文進行動態(tài)限流的算法,其中上下文指各任務(wù)在過去一秒內(nèi)使用的帶寬,此外,算法還考慮到了任務(wù)數(shù)量、任務(wù)剩余大小、任務(wù)保底帶寬等因素,性能相比原來的靜態(tài)限流算法有顯著提升。
相關(guān)代碼分析
perPeerRateLimit 配置項最終賦值給 peerTaskConductor 的pt.limiter ,由 peerTaskConductor 的 DownloadPiece() 函數(shù)里進行限速,pt.waitLimit() 進行實際限流工作,底層調(diào)用 Go 自帶的限流函數(shù) WaitN() 。
TotalRateLimit 配置項則在創(chuàng)建 Daemon 時被賦值給 pieceManager 的pm.limiter ,在 pieceManager 的 DownloadPiece() 和 processPieceFromSource() 函數(shù)中用到的 pm.limiter ,而這兩個函數(shù)都會由 peerTaskConductor 調(diào)用,也就是說 P2P 下載會先進行總限速,之后再進行每個任務(wù)單獨限速。

根據(jù)以上分析,Dragonfly 進行任務(wù)限速的邏輯為,每個peer task(peerTaskConductor)會有單獨的限速 perPeerRateLimit ,同時 pieceManager 會有 TotalRateLimit 的總限速,以此達到單任務(wù)單獨限流,同時限制所有任務(wù)總帶寬的效果。
優(yōu)化方案
為了解決此前靜態(tài)限流算法總帶寬利用率不佳的缺點,需要將其改進為動態(tài)限流算法,即總帶寬限速仍恒定,但每個任務(wù)的單獨帶寬限速需要根據(jù)上下文適度、定期調(diào)整,已達到最大化利用總帶寬、同時相對公平分配帶寬的目的。
在經(jīng)過數(shù)個改版后,最終我們確定了根據(jù)上下文進行限流的 sampling traffic shaper 動態(tài)限流算法。具體方案為,每個任務(wù)的單任務(wù)限流交由 TrafficShaper 組建進行統(tǒng)一管理, TrafficShaper 維護當前正在運行的所有任務(wù),并且定期(每秒)更新這些任務(wù)的帶寬。
具體來說,上下文指每個任務(wù)在上一秒使用的帶寬、每個任務(wù)的剩余大小、任務(wù)數(shù)量、任務(wù)保底帶寬(不能低于 pieceSize )等因素, TrafficShaper 會根據(jù)這些上下文公平地、效率最大化地為每個任務(wù)分配其下一秒的帶寬(具體分配方案詳見下一小節(jié)),實現(xiàn)動態(tài)限流的效果。
優(yōu)化實現(xiàn)
定義 TrafficShaper 接口如下:
// TrafficShaper allocates bandwidth for running tasks dynamically
type TrafficShaper interface {
// Start starts the TrafficShaper
Start()
// Stop stops the TrafficShaper
Stop()
// AddTask starts managing the new task
AddTask(taskID string, ptc *peerTaskConductor)
// RemoveTask removes completed task
RemoveTask(taskID string)
// Record records task's used bandwidth
Record(taskID string, n int)
// GetBandwidth gets the total download bandwidth in the past second
GetBandwidth() int64
}
該接口有兩種實現(xiàn),第一種是 samplingTrafficShaper 即基于上下文的 traffic shaper ,第二種是 plainTrafficShaper 只記錄帶寬使用情況,除此之外不做任何動態(tài)限流工作,用于和 samplingTrafficShaper 對比性能提升。
同時,將相關(guān)配置項修改為如下內(nèi)容:
# 下載服務(wù)選項。 download: # 總下載限速。 totalRateLimit: 1024Mi # 單個任務(wù)下載限速。 perPeerRateLimit: 512Mi # traffic shaper類型,有sampling和plain兩種可選 trafficShaperType: sampling

Traffic shaper 的具體運行邏輯為,由peerTaskManager維護trafficShaper,在創(chuàng)建peerTaskManager時,根據(jù)配置初始化trafficShaper,并且調(diào)用Start()函數(shù),啟動trafficShaper,具體來說,新建time.NewTicker,跨度為 1 秒,也即每秒trafficShaper都會調(diào)用updateLimit()函數(shù)以動態(tài)更新所有任務(wù)的帶寬限流。
updateLimit() 函數(shù)會遍歷所有運行中的任務(wù),得出每個任務(wù)上一秒消耗的帶寬以及所有任務(wù)消耗的總帶寬,隨后根據(jù)任務(wù)上一秒使用的帶寬、任務(wù)剩余大小等因素,按比例分配帶寬,具體來說首先根據(jù)上一秒該任務(wù)使用帶寬以及該任務(wù)剩余大小的最大值確定下一秒該任務(wù)帶寬,接著所有任務(wù)帶寬根據(jù)總帶寬按比例縮放,得到下一秒的真實帶寬;同時需要確保每個任務(wù)的帶寬不低于該任務(wù)的 pieceSize ,以免出現(xiàn)持續(xù)饑餓狀態(tài)。
在 peerTaskManager 的 getOrCreatePeerTaskConductor() 函數(shù)中,若新建任務(wù),需要帶寬,那么調(diào)用 AddTask() 更新所有任務(wù)的帶寬,即按照已有任務(wù)的平均任務(wù)分配帶寬,然后再根據(jù)總帶寬上限將所有任務(wù)的帶寬等比例進行縮放;根據(jù)平均帶寬分配新任務(wù)帶寬的優(yōu)勢為,避免了已經(jīng)有一個任務(wù)占滿了所有帶寬,有新任務(wù)進來時,帶寬會被壓縮到很小 **的情況;同時,不是平均分配帶寬,而是按需等比例分配,可以確保帶寬需求量大的任務(wù)仍然帶寬最多。在 peerTaskManager 的 PeerTaskDone() 函數(shù)中,任務(wù)完成,不再占用帶寬,調(diào)用 RemoveTask() 按比例擴大所有任務(wù)的帶寬。
最后, peerTaskManager 停止時,調(diào)用 Stop 函數(shù),停止運行 traffic shaper 。
優(yōu)化結(jié)果
測試 traffic shaper 相比原有的靜態(tài)限流策略在單個任務(wù)、多個任務(wù)并發(fā)、多個任務(wù)交錯等多種情況下的性能提升,測試結(jié)果如下:

注:若不特殊注明,單任務(wù)限流為4KB/s,總限流為10KB/s
可以看到, traffic shaper 在單任務(wù)、多任務(wù)不相交、單任務(wù)低帶寬等情況下相比靜態(tài)限流策略性能提升明顯,為 24%~59% 。在多個任務(wù)并發(fā)、多個任務(wù)交錯等情況下和靜態(tài)限流策略性能相當。綜上,實驗證明 sampling traffic shaper 能很好地解決任務(wù)數(shù)量較少時總帶寬被大量浪費的情況,同時在任務(wù)數(shù)量較多以及其他復(fù)雜情況時依舊能保證和靜態(tài)限流算法持平的效果。
PR 鏈接(已合并): github.com/dragonflyos…
以上就是Dragonfly P2P 傳輸協(xié)議優(yōu)化代碼解析的詳細內(nèi)容,更多關(guān)于Dragonfly P2P 傳輸協(xié)議的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
JS前端二維數(shù)組生成樹形結(jié)構(gòu)示例詳解
這篇文章主要為大家介紹了JS前端二維數(shù)組生成樹形結(jié)構(gòu)示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-09-09
JS創(chuàng)建對象常用設(shè)計模式工廠構(gòu)造函數(shù)及原型
本篇帶來你一定熟知的、用于創(chuàng)建對象的三種設(shè)計模式:工廠模式、構(gòu)造函數(shù)模式、原型模式,有需要的朋友可以借鑒參考下,希望能夠有所幫助2022-07-07
詳解JavaScript中數(shù)組的相關(guān)知識
這篇文章主要介紹了JavaScript中中數(shù)組的相關(guān)知識,是JS入門學(xué)習(xí)中的基礎(chǔ)知識,需要的朋友可以參考下2015-07-07
Three.js概述和基礎(chǔ)知識學(xué)習(xí)
這篇文章主要為大家介紹了Three.js概述和基礎(chǔ)知識學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-05-05
AntDesignPro使用electron構(gòu)建桌面應(yīng)用示例詳解
這篇文章主要為大家介紹了AntDesignPro使用electron構(gòu)建桌面應(yīng)用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2022-10-10
實現(xiàn)一個簡單得數(shù)據(jù)響應(yīng)系統(tǒng)
這篇文章主要介紹了實現(xiàn)一個簡單得數(shù)據(jù)響應(yīng)系統(tǒng),文章介紹的數(shù)據(jù)響應(yīng)系統(tǒng)會用到Dep,其實,這就是一個依賴收集的容器, depend 收集依賴, notify 觸發(fā)依賴,下面來看看詳細的內(nèi)容結(jié)介紹,需要的朋友可以參考一下2021-11-11

