在工作中,我曾與小伙伴討論過這樣一個實際問題:數據制作流程產生了一份需要上線的文件,而線上有數十臺甚至上百臺機器,有什么樸素的辦法以盡可能快的速度將文件分發到指定的機器上嗎?根據作者已有的知識,分享一個內網大文件分發的想法。本文未討論組播/廣播等方法,因為作者還不會😅。
定義內網、大文件、樸素
本文中提到的內網為網絡延遲基本在毫秒級、基本不丟包、傳輸基本不出錯的網絡環境。本文提到的樸素辦法可以認為是依托現有條件,以無需額外增加硬件、也無需過多維護開銷為前提的辦法。本文提到的大文件約為1G-1T的文件,小文件直接傳輸也可樸素地快速完成,超大文件估計需要硬件支持。
- 小文件(1M以內): 寫個簡單循環,逐個向目標機器發送即可
- 中等文件(1M-1G): 一般可以搭建文件服務器,目標機器并發地從文件服務器拉取文件
- 大文件(1G-1T): 本文討論的方法
- 超大文件(1T以上): 或許應當求助于技術部門協助解決
一對一文件復制
目前常見的企業級固態硬盤順序讀寫速度一般能達到3-5GB/s,為了能快速讀取和寫入文件,可以將文件按固定大小(比如4MB)分塊,并采用并行/并發的方式進一步提高吞吐率。通常來說,讀寫文件的開銷不會達到CPU性能瓶頸,所以要么磁盤IO被打滿,要么網卡被打滿,傳輸速度一般可以達到極致。如果有高性能硬件支持,還可以考慮RDMA等技術,但如果使用頻率不高,這樣做的性價比估計會很低。
鏈式傳輸
即使一對一復制可以使傳輸速度達到極限,但逐個分發的總時長還是會隨著目標機器數量的增加而線性增加,而實際需求是所有數據都就緒后才能繼續操作,還有什么辦法提速嗎?
在一對一復制過程中,發送方確實已經很賣力地讀取并發送數據了,它的出口帶寬也無法支撐同時向幾十個目標機器發送數據。但接收方的出口帶寬是閑置的,如果由接收方負責將數據繼續分發下去,最終形成一條傳輸鏈,就可以將所有硬件資源全部利用起來了。示意圖如下,其中M0
為數據源,M1 ~ Mn
為目標機器。
樹型傳輸
如果僅有幾十臺機器,鏈式傳輸基本可以達到令人滿意的效果了,但如果機器數量更多呢?由于作者沒有機器做測試,所以這里只能做一些思想實驗。考慮一個足夠長但粗細不一的水管,要想將水送的足夠遠,水源就要不斷地增加壓力,遠離水源的水管有一點波動,壓力就會傳導到其前面的每個位置(類似水錘效應),所以這樣做估計不行。
如果可以讓渡一些傳輸速度,將傳輸鏈換成傳輸樹,則最大傳輸長度就會大大降低,對于數千臺機器的集群也可以在十幾層內解決了。示意圖如下
這種情況下,最大傳輸速度受限于出帶寬的一半,但我們并未限制只能有一棵傳輸樹😏,將每個葉結點和內部結點交換,可以再構成一棵傳輸樹,還是能把帶寬都跑滿。
速度控制
一般來說,整個機房的網絡設備會被多個業務共享,一個業務傳輸數據占用大量帶寬是不禮貌的。一個優秀的工具既應該在需要時跑到慢速,也應該具備整體限速的功能。在上述想法中,由于數據發送源只有一個,很容易控制整體傳輸速度,而如果考慮p2p文件共享的思路,在整體限速功能上需要花費一定的精力。
具體實現
上面包了這么多餃子,實際上就是為了嘗嘗作者自己做的醋好不好吃。作者在fcopy項目中實現了上述想法的大部分功能,fcopy
依賴于coke,而coke
是C++ Workflow的C++ 20協程版本,所以該項目的主要目的是證明相比于直接使用C++ Workflow
- 使用
coke
封裝后不會對性能有太大影響 - 使用
coke
可以讓代碼量更少,也無需到處傳遞上下文信息 - 使用
coke
時,函數不會因需要異步回調而截斷,而是在合理的流程處自然分段
結語
有了這些辦法,您每次上線的時間從3小時縮短到了5分鐘,可以早點下班回家,陪妻子或丈夫吃了晚飯、給孩子輔導完功課、給父母和岳父母打了電話,發現還有半個小時的時間可以利用,您打開了coke,提出了一個富有建設性的issue、發起了一個極具創造性的PR、點了一個具有歷史意義的Star,幫助了更多的人可以更好地工作和生活,這是多美好的一件事呀。