用 LFS 做極簡高效的流媒體服務
LFS 非常非常快的文件系統,可以同時存儲海量大文件和小文件,高并發。這里用來實現一個極簡高效的流媒體服務。并詳細部署一個圖片服務器。但,LFS 不單用來存儲此類文件,其擁有 nosql 的特性,事實上,我喜歡用來存儲分詞索引(存儲會不斷增長的數據,如:一個詞匯對應許多的記錄 ID)。
示例可在:github git@osc 找到,包含一個詳細的圖片服務器
圖片服務演示(簡單部署了 php 的服務):
瀏覽圖片:http://lfs2-ikcourage.myalauda.cn/testimage/image.php?type=read&id=1
上傳圖片:http://lfs2-ikcourage.myalauda.cn/testimage/upload.php (圖片會自動周期性刪除)
先做一個音樂、視頻、圖片等媒體文件的上傳和下載示例。都只需要一行。
FileInputStream inputStream = new FileInputStream(file); fileId = LFS_Stream.writeStream(FILE_NAME, fileId, inputStream, file.length());
OK,一個媒體文件上傳成功后會返回一個文件 ID。
即使是一個比較大的視頻,比如大于 10G,也依然只有這一行代碼,并不需要做切片存儲,并且無需擔心內存使用。(大文件的上傳也變的如此簡單)
下載:
//讀到 readStream 中 LFS_Stream.readStream(FILE_NAME, fileId, readStream);
沒錯,就是這一行啦,根據文件 ID 讀取文件,把數據通知給 readStream。
那么,我們來看看 readStream 做了什么。(這就是一個完整的媒體服務的示例)
IReadStream readStream = new IReadStream() { public boolean init(long fileId, int size, long sizeTotal, long sizeTotalRead, long offset) { response.setIntHeader("Content-Length", (int)sizeTotalRead); response.setHeader("Cache-Control", "max-age=604800"); response.setIntHeader("Etag", 0); return true; } //參數好多啊,不要被嚇到了,只有前兩個是你需要用的 //其他的只是為了避免全局變量而已(如果需要的話,所幸,絕大多數場景都不會需要) public boolean parseData(byte[] b, int bytesAvalibale, int size, long sizeTotal, long sizeTotalRead, long sizeTotalReaded, long offset) { try { //這里應實現自己的數據輸出,比如輸出到 http response.getOutputStream().write(b, 0, bytesAvalibale); return true; } catch (Exception e) {} return false; } };
init 只會在 parseData 前調用一次,用來獲取文件的真實大小,比如我們可能需要為 http 添加 Content-Length 的頭等。
parseData 每次都會調用,把緩沖中的數據輸出到 http 的輸出流中,bytesAvalibale 是緩沖中的有效字節大小。
到這里你會發現,parseData 中是流式輸出數據的,所以這保證了音樂、視頻等流媒體的流暢度,并且也有效降低了服務器的內存占用。因為緩沖的大小是非常小的,你可以理解為 Socket 的緩沖大小,并且可調節,默認大小 2K(注:不是帶寬的大小,這是一個遠高于帶寬的值)。
這意味著一臺服務器,即使提供視頻服務也游刃有余。1W 人同時觀看視頻只需要不到 20M 的內存(資源過剩的浪費)。
既然是流媒體服務(圖片也是,在瀏覽器中快速刷新,會看到圖片一點點的渲染),那么斷點下載很重要,斷點上傳也很重要。
斷點下載:
//多了兩個參數 LFS_Stream.readStream(String fileName, long fileId, IReadStream readStream, long offset, long sizeTotalRead);
還是那一行,只不過多了一個偏移和讀取大小而已,很簡單不是么。
斷點上傳:
//多了兩個參數 LFS_Stream.writeStream(String fileName, long fileId, InputStream inputStream, long sizeTotalWrite, long offset, long sizeTotal);
sizeTotalWrite 是要寫的大小,sizeTotal 是文件的總大小。
這句話的含義是:把文件的總大小設置為 sizeTotal,并且在 offset 處寫入 sizeTotalWrite 大小的內容。
上傳下載就是如此的簡單。
LFS 的啟動
首先從 github 獲取一個 LFS
然后以守護模式運行 LFS --dir [存儲目錄] --daemon
這就好了
最后,如果需要自定義文件名的話,可以使用 LFS 的索引(即:key, value)。參考上一篇內容。
來自:http://my.oschina.net/courage/blog/502340