史若曦,馬俊才,田園靜,張薦轅
1(中國科學院 計算機網(wǎng)絡信息中心,北京 100190)
2(中國科學院 微生物研究所,北京 100101)
3(中國科學院大學,北京 100049)
隨著微生物數(shù)據(jù)的快速增長,研究者越來越重視微生物大數(shù)據(jù)的高效管理和分析.國家科學微生物數(shù)據(jù)中心建立了以云計算為基礎的生物信息分析的數(shù)據(jù)平臺[1],用戶可以登陸網(wǎng)站,根據(jù)需求在線使用生物信息分析工具.由于網(wǎng)站面向所有用戶開放,一般生物文件較大,HTTP 協(xié)議下云服務器端對文件接收大小也有一定的限制,用傳統(tǒng)的插件技術、表單直傳等方法進行文件上傳時效率過低,網(wǎng)絡波動或瀏覽器異常等突發(fā)情況導致上傳中斷時也需要對整體文件進行重復上傳[2].所以,本文設計了一個與分片傳輸技術相結合的多線程傳輸方案,保證用戶數(shù)據(jù)可以安全、快速的傳遞至云平臺.
傳統(tǒng)的基于Web 文件上傳的方法有Form 表單上傳,該方法要求表單里包含一個類型為file的輸入文件框,與JavaScript 等技術結合,實現(xiàn)文件的上傳操作.插件技術是另一種實現(xiàn)Web 端文件上傳的方法,主要利用ActiveX、Huploader 等一系列插件實現(xiàn)文件上傳,但是該技術容易因瀏覽器的設置導致插件運行失敗,因此只適合在內(nèi)網(wǎng)此類安全的內(nèi)部環(huán)境使用[3].
除此之外,文獻[4]提出了一種斷點續(xù)傳方案,大文件在Web 端分片后,通過計算出每片的MD5 值來做為其唯一標識符.當網(wǎng)絡異常發(fā)生傳輸中斷時,由唯一標識符來確定斷點續(xù)傳從分片相應部分開始.文獻[5]提出了一種多線程控制的方式.該方法在傳輸多個不同文件時,為每個文件開啟一個線程來控制內(nèi)容文件和配置文件,雖然在傳輸多個文件時有明顯優(yōu)點,但針對單個文件時效率不高.文獻[6,7]采用雙線程分別記錄文件內(nèi)容和內(nèi)容偏移量的方法,該方法在實現(xiàn)斷點續(xù)傳的情況下犧牲了傳輸效率.文獻[8]在線程選擇上采取逐步增加的方式,雖然提高了傳輸效率,但是逐步增加試探,在網(wǎng)絡承載率大的情況下仍然有一定程度的資源浪費.
盡管上述技術已經(jīng)對斷點續(xù)傳、重復文件上傳等問題進行了一定程度的解決,但針對如何提升大文件傳輸速率這一問題仍存在不足.部分技術為了實現(xiàn)斷點續(xù)傳,采用多線程來記錄文件傳輸時產(chǎn)生的相關配置文件,造成了帶寬資源的浪費.一些技術雖然提出利用多線程來實現(xiàn)文件并發(fā)傳輸,但并沒有具體提出如何選擇并發(fā)線程數(shù)來保證盡可能高效的利用網(wǎng)絡帶寬.
因此本文提出了一種根據(jù)帶寬時延積(BDP)來選擇并發(fā)線程數(shù)的文件傳輸方案,與分片上傳、斷點續(xù)傳技術相結合,滿足生物數(shù)據(jù)分析平臺用戶的需要,實現(xiàn)生物數(shù)據(jù)的穩(wěn)定上傳.
在實際的傳輸過程中,數(shù)據(jù)每份發(fā)送后無法立即得到確認,這些在信道中傳輸?shù)€未被確認的數(shù)據(jù)量通過用帶寬時延積(BDP)來表示,它是衡量網(wǎng)絡鏈路能力和承載能力的關鍵指標.窗口機制就是防止數(shù)據(jù)量超過接收端確認處理能力的一種措施,它通過限定窗口大小的方式來進行TCP的流量控制和擁塞控制.滑動窗口和固定窗口是常用的兩種窗口機制,但是由于TCP 報文頭中窗口字段大小只有16 位,無論哪種方式,TCP 窗口最大值都為64 KB.在理想的寬帶利用率下,帶寬時延積應與TCP 窗口大小一致.在1000 Mb/s的網(wǎng)絡帶寬下,只有往返時延在小于0.448 ms 時,BDP才會小于64 KB,此時能夠有效利用帶寬.但是在實際的網(wǎng)絡情況下,要想達到這么小的往返時延幾乎是不可能的.因此,對于并發(fā)TCP 連接,通過同時建立多條連接,并發(fā)n條連接就相當于將窗口大小擴大了n倍,從而能有效的提高傳輸速率[9].
根據(jù)前文對TCP 窗口機制與帶寬時延積的理論分析可知,為了實現(xiàn)高效的利用網(wǎng)絡帶寬,減少數(shù)據(jù)等待確認時的資源浪費,TCP 發(fā)送窗口大小應與帶寬時延積一致.然而受到報文頭字段大小限制,要想在生物數(shù)據(jù)分析平臺用戶的網(wǎng)絡條件下提高文件傳輸效率,應采用多線程的方法建立多條TCP 連接,擴大傳輸窗口.
對于并發(fā)TCP 來說,理想的并發(fā)數(shù)N可以用式(1)計算.
其中,BDP為帶寬時延積,由網(wǎng)絡帶寬與傳輸時延(RTT)共同確定,其計算方式為BDP=帶寬 ×RTT,MaxWindowSize為最大窗口數(shù).
本文綜合分片傳輸和斷點續(xù)傳等技術,提出一種根據(jù)用戶網(wǎng)絡帶寬時延積選擇最佳并發(fā)線程數(shù)的傳輸方案.該方案采用部分功能函數(shù),實現(xiàn)Web 端的文件分片傳輸,并根據(jù)MD5的計算原理,采用分片計算后整合的方式,最終得到原文件的MD5 值.在解決相同文件上傳時,使用MD5 校驗來檢測服務器端是否已經(jīng)存儲該文件[10],提高傳輸效率.本文的關鍵在于設計了一個多線程創(chuàng)建方式,在用戶打開網(wǎng)站時獲得此時的網(wǎng)絡狀態(tài),根據(jù)此參數(shù)得到當前網(wǎng)絡狀態(tài)下用戶進行上傳操作時可使用的最大線程數(shù),進而提升了文件的上傳效率.文件發(fā)送流程如圖1所示.
圖1 客戶端文件發(fā)送流程
線程數(shù)的選擇主要依據(jù)網(wǎng)絡帶寬和傳輸時延,該參數(shù)可以在用戶與服務器建立連接時測定,通過BpsDataPerInterval 方法,獲取連接服務器時的帶寬傳輸時延RTT,根據(jù)上文公式,確定并發(fā)數(shù)poolSize=(RTT×帶寬)/(64×1024),存儲在poolSize函數(shù)中,在進行上傳時,啟動與服務器的線程連接.
因為服務器端會限制每次上傳文件的大小,所以需要在前端指定每片文件的值.如果分片較小,則分片數(shù)大,會導致多次建立傳輸請求,增大開銷;如果分片較大,則會降低靈活度[11].綜合數(shù)據(jù)分析平臺服務器端的設置要求,本文設置每片的大小為2 MB,即chunkSize=1024×1024×2,此外還需要的參數(shù)有,chunkNumber 表示分片序數(shù),chunks 表示分片總數(shù),用于服務器端的文件合成.其合并流程如圖2所示.
圖2 服務器端文件合并流程
生物信息分析平臺中的分析工具所需生物數(shù)據(jù)文件大都在600 MB 以上,甚至大到十幾GB,如果采用整體計算文件MD5 值的方式,容易導致內(nèi)存占用過大,Web 端異常崩潰等情況,計算效率也相對較低.
由于MD5的計算特性,分片計算每部分的MD5值后,再進行合并不改變原文件的MD5 值[12],因此本文采用新的計算方式.將文件分片后逐個傳入spark.appendBinary()方法來計算、最后通過spark.end()方法輸出MD5.這種方法節(jié)約內(nèi)存開銷,在計算大文件MD5 值效果更好.根據(jù)前文設置,分片大小為2 MB,綜合文件大小得出總片數(shù),然后設置file.cmd5=true,即文件狀態(tài)改為MD5 計算.接著逐片讀取分片信息,并計算MD5 值,由spark.end()得出所有值后,將總文件的MD5 值賦給file.5;,作為該文件唯一標識,為秒傳和斷點續(xù)傳操作提供方便.最后取消計算狀態(tài),并開始上傳文件.其相關代碼如下:
//計算MD5
computeMD5(file) {
chunkSize=2 097 152,//2 MB
chunks=Math.ceil(file.size/chunkSize),
currentChunk=0,
spark=new SparkMD5.ArrayBuffer(),
fileReader=new FileReader();
let time=new Date().getTime();
file.cmd5=true;//文件狀態(tài)為“計算md5…”
fileReader.onload=(e)=> {
spark.append(e.target.result);
currentChunk++;
if (currentChunk< chunks) {
loadNext();
} else {
console.log('finished loading');
let md5=spark.end();//得到md5
file.uniqueIdentifier=md5;//將文件md5 賦值給文件唯一標識
file.cmd5=false;//取消計算md5 狀態(tài)
file.resume();//開始上傳
}
loadNext();
}
受異常情況中斷后,整體文件重傳需要很大的代價,本文利用已經(jīng)計算得出的文件MD5 值作為文件的特殊標識符,在進行文件重傳時,通過MD5 校驗判斷文件是否已經(jīng)上傳[12],然后再進行傳輸操作.本文采用checkChunkUploadedByResponse()函數(shù)響應后臺返回的信息,并檢測分片信息是否上傳完整.分片上傳前,前端會向后端發(fā)送一個攜帶文件信息的get 請求.如果文件已經(jīng)在服務器端存儲,則返回obj.isExist,后續(xù)上傳操作不需要繼續(xù)執(zhí)行.如果返回的是文件分片信息,則表示該部分已經(jīng)上傳,執(zhí)行續(xù)傳操作.其相關代碼如下:
//續(xù)傳實現(xiàn)
checkChunkUploadedByResponse:(chunk,message)=> {
let obj=JSON.parse(message);
if (obj.isExist) {
this.statusTextMap.success='秒傳文件';
return true;
}
return(obj.uploaded||[]).indexOf(chunk.offset + 1)>=0
},
//檢測斷點和MD5
public function checkFile()
{
//檢測文件MD5是否已經(jīng)存在
$rs=$this->checkMd5($identifier,
$this->fileInfo['totalSize']);
if ($rs['isExist']===true) {
return $rs;
}
//檢查分片是否存在
$chunkExists=[];
for ($index=1;$index <=$totalChunks;$index++){
if (file_exists("{$filePath}_{$index}")) {
array_push($chunkExists,$index);
}
}
本文采用的是多線程傳輸,為了保證傳輸?shù)臏蚀_性,需等待所有分片傳輸成功再進行合并.當傳輸?shù)姆制瑪?shù)等于總分片數(shù)chunks 時,會向后臺發(fā)送合并請求.onFileSuccess()方法接收從后臺返回的response 包含了是否需要合并的指令merge,如果resp.merge===true,則向后端發(fā)送合并請求.前端將文件的唯一 ID和拆分總數(shù)(或要傳遞的更多參數(shù))發(fā)送到合并文件的后端.后端受到合并指令后開始進行文件合并其相關代碼如下:
//文件合并
public function merge()
{
$filePath=self::$tmpDir.DIRECTORY_SEPARATOR.$this->fileInfo['identifier'];
$totalChunks=$this->fileInfo['totalChunks'];//總分片數(shù)
$filename=$this->fileInfo['filename];//文件名
$done=true;
//檢查所有分片是否都存在
for ($index=1;$index <=$totalChunks;$index++){
if(!file_exists("{$filePath}_{$index}")) { $done=false;
break;
}
}
if ($done===false) {
return $this->message(1005,'分片信息錯誤');
}
//如果所有文件分片都上傳完畢,開始合并
$timeStart=$this->getmicrotime();//合并開始時間
$saveDir=self::$saveDir.
DIRECTORY_SEPARATOR.date('Y-m-d');
if (!is_dir($saveDir)) {
@mkdir($saveDir);
}
$uploadPath=$saveDir.DIRECTORY_SEPARATO R.$filename;
if (!$out=@fopen($uploadPath,"wb")) {
return $this->message(1004,'文件不可寫');
}
if (flock($out,LOCK_EX)) {// 進行排他型鎖定
for($index=1;$index<=$totalChunks;$index++) {
if(!$in=@fopen("{$filePath}_{$index}","rb")) {
break;
}
while ($buff=fread($in,4096)) {
fwrite($out,$buff);
}
@fclose($in);
@unlink("{$filePath}_{$index}");//刪除分片
}
flock($out,LOCK_UN);// 釋放鎖定
}
@fclose($out);
return $res;
}
根據(jù)實際網(wǎng)絡情況,本文進行了帶寬時延積BDP為75 KB和135 KB 兩種情況下的文件傳輸測試.
如表1所示,當BDP大于64 KB 時,該網(wǎng)絡情況可以進行多線程傳輸,其效率相較單線程傳輸有一定的提高.
表1 不同BDP 下傳輸耗時的測試結果
本文提出利用帶寬時延積和最大窗口數(shù)計算得到網(wǎng)絡最大承載率,來決定并發(fā)線程數(shù)的文件上傳方法,充分利用網(wǎng)絡帶寬資源,采用MD5 值標識已經(jīng)上傳過的文件,實現(xiàn)生物數(shù)據(jù)分析平臺用戶的文件高速上傳,節(jié)約了時間成本.該方法解決了一般分片上傳過程中,無法確定并發(fā)線程數(shù)的問題,能夠提高大文件的上傳效率,增強傳輸穩(wěn)定性.