簡約的CDN:minicdn
MiniCDN
一般來說會推薦采用 qiniu 或者 upyun,又或者是 amazon 之類大公司的 cdn 服務,不過當需要一些自己實現的場景,比如企業內部軟件的加速,就需要一個私有的 CDN 了。
極簡內容分發系統是我在公司里面的一個項目,最近把他開源出來了。可能其他企業或者組織也需要一個類似的東西。
通常來說 CDN 分為 push 和 pull 兩種方式,push 比較適合大文件,pull 適合小一些的文件,但是使用起來比 push 要簡單的多。
MiniCDN 采用的就是 pull 這種方式,目前的實現方式是所有緩存的文件存儲在內存中,使用 LRU 算法淘汰掉就的文件,鏡像的文件受限于緩沖區的大小(目前的緩沖區是512M),如果超過了這個緩沖器大小,就沒有加速的效果了。
沒有所有的智能 DNS,直接用的是最簡單的 http redirect。 還沒寫負載均衡,所以 redirect 的時候,就是隨機返回一個節點(簡單粗暴)
MiniCDN 分為 manager 和 peer。都是寫在一個程序里。
我平常用的時候,就只開一個 minicdn 的 Manager 來加速我的后端服務器。如果沒有節點的話,manager 就會把自己當成一個節點。然后當有特別大的下載即將要沖擊我的服務器的時候。我就會找很多的同事,將 minicdn 部署到他們平常用的電腦上(window系列, 因為是golang語言寫的,什么平臺的程序都能編譯的出來)。這樣我在短時間內就擁有了一個性能不錯的 cdn 集群(充分利用同事的資源)。當下載沖擊結束的時候,在把這些節點撤掉就可以了。相當省事
技術優勢
MiniCDN 使用了谷歌開源出來的 groupcache 框架,目前dl.google.com后臺就用到了groupcache,性能而言遠超那些squid或者nginx-proxy-cache.
groupcache 的數據獲取過程很有意思,我把他翻譯了過來
groupcache 的運行過程
查找foo.txt的過程(節點#5 是N個節點中的一個,每個節點的代碼都是一樣的)
-
判斷foo.txt是否內存中,并且很熱門(super hot),如果在就直接使用它
-
判斷foo.txt是否在內存中,并且當前節點擁有它(譯者注:一致性hash查到該文件屬于節點#5),如果是就使用它
-
在所有的節點中, 如果foo.txt的擁有者是節點#5,就加載這個文件。如果其他請求(通過直接的,或者rpc請求),節點#5會阻塞該請求,直接加載完畢,然后給所有請求返回同樣的結果。否則使用rpc請求到擁有者的節點,如果請求失敗,就本地加載(譯者注:這種方式比較慢)
groupcache 是2013年寫出來的,軟件也不怎么更新了。里面的 HTTPPool 還有兩個問題一直沒有修復,這兩個問題直接影響到節點之間不能交換數據。因為官方不用 groupcache 的這部分,所以連用戶提的 issue 都不修(真是蛋疼)
https://github.com/codeskyblue/groupcache 是我fork的,把這兩個問題修復了,雖然提了pr,不過感覺他們一時半會不會merge的。
受 python-celery 的啟發,我實現了 peer 退出時候的兩種狀態(Warm close and Code close)。 Warn close可以保證黨節點不在服務的時候才退出。Code close就是強制退出,下載者可能會發現下載中斷的問題。
架構
-
M: Manager
-
負責維護Peer的列表,每個peer會去Manager同步這個列表。
-
所有的請求會先請求到manager, 然后由manager重定向到不同的peer
-
P: Peer
-
提供文件的下載服務
-
Peer之間會根據從manager拿到的peer列表,同步文件
Manager 與 Peer 是一對多的關系
[M] |`------+--------+---...... | | |[P] [P] [P] ....
Run Manager
命令行啟動
./minicdn -mirror http://localhost:5000 -addr :11000 -log cdn.log
-
對網站http://localhost:5000進行鏡像加速
-
監聽11000端口
-
日志存儲在cdn.log中
源站的所有下載地址,最好都改成這個http://localhost:5000/something
Run Peer
命令行啟動
./minicdn -upstream http://localhost:11000 -addr :8001
-
指定Server地址http://localhost:11000
-
監聽8001端口