分布式文件快速搜索-技術(shù)細(xì)節(jié)分析(開(kāi)源/并行)
2010-07-26 18:37 靈感之源 閱讀(5639) 評(píng)論(20) 收藏 舉報(bào)
系列文章
1.分布式文件快速搜索(多計(jì)算機(jī)并行/多種算法)
2.分布式文件快速搜索的設(shè)計(jì)與實(shí)現(xiàn)(開(kāi)源/分布式計(jì)算/并行)
3.分布式文件快速搜索-技術(shù)細(xì)節(jié)分析(開(kāi)源/并行)
前言
在上一篇文章中,對(duì)分布式文件快速搜索的設(shè)計(jì)與實(shí)現(xiàn)進(jìn)行了說(shuō)明。今天,將對(duì)具體的實(shí)現(xiàn)細(xì)節(jié)進(jìn)行分析。
文件的檢索
-
文件獲取
1). 一般地,用Directory.GetDirectories()加上SearchOption.AllDirectories來(lái)獲取某個(gè)的目錄下的所有文件(包括任意層子文件)。但在這里會(huì)采用自行遞歸獲取,每獲取一層目錄的文件,都會(huì)預(yù)先根據(jù)SearchTypes條件(如文件大小、文件名、修改時(shí)間、屬性等)來(lái)碰撞。具體參看WorkV7FindFileRunner.FindLocal方法。
2). 相比遞歸獲取文件列表,我們可以直接讀取物理磁盤的MFT(Master File Table)主文件表,這樣不需要逐個(gè)目錄遞歸,直接順序獲取所有文件夾和文件記錄,速度可以比遞歸快10倍以上。在Windows NT便引入的NTFS文件系統(tǒng),擁有USN Journal(文件日志),每個(gè)文件的變化(添加、修改、刪除、更名等)都會(huì)被記錄,在通過(guò)MFT獲取所有文件后,每次運(yùn)行,只需要判斷一下系統(tǒng)的變化并記錄便可,無(wú)需重新掃描整個(gè)磁盤。
-
哈希獲取
在使用SearchTypes條件過(guò)濾完成后,使用MD5CryptoServiceProvider來(lái)獲取MD5哈希值。當(dāng)然,你可以使用SHA1CryptoServiceProvider計(jì)算哈希值,如果你覺(jué)得MD5不可靠,具體參看WorkUtils.HashMD5File。
實(shí)際的哈希獲取,會(huì)使用緩存。緩存的實(shí)現(xiàn)使用快速二進(jìn)制序列化,原理是判斷緩存數(shù)據(jù)庫(kù)是否存在一樣的文件信息(文件名、大小和修改時(shí)間),如果匹配,則返回已經(jīng)存在的哈希值,否則就獲取新的哈希值。具體參看LocalHashStorage。
文件的匹配/比較
文件的匹配有3種方式:完全一致、包含以及全文索引。
-
完全一致
完全一致,直接使用哈希值比較。
-
包含
適用于運(yùn)行時(shí)搜索,判斷文本文件中包含的內(nèi)容。目前直接使用File.ReadAllText(file).IndexOf(keyword, StringComparison.InvariantCultureIgnoreCase)來(lái)判斷,可能遇到的問(wèn)題應(yīng)該是文件太大導(dǎo)致內(nèi)存溢出。
-
全文索引
可索引文件的全文內(nèi)容會(huì)自動(dòng)緩存,支持自定義擴(kuò)展接口IFileContentIndex,目前內(nèi)置了微軟的IFilter實(shí)現(xiàn)。具體參看LocalFileContentIndexStorage。
并行計(jì)算的處理
實(shí)現(xiàn)
因?yàn)椴皇?NET3.5/4,沒(méi)有PPL,只能模擬并行,來(lái)源參看分布式文件快速搜索v7.0(多計(jì)算機(jī)并行/多種算法)。原理是使用ThreadPool.QueueUserWorkItem各個(gè)任務(wù),使用ManualResetEvent記住每個(gè)任務(wù)的狀態(tài),并用WaitHandle.WaitAll等待所有任務(wù)完成。具體參看ParallelProcessor.ExecuteParallel。
ExecuteParallel
{
if (methods.Length > 0)
{
// Initialize the reset events to keep track of completed threads
ManualResetEvent[] resetEvents = new ManualResetEvent[methods.Length];
// Launch each method in it's own thread
for (int i = 0; i < methods.Length; i++)
{
resetEvents[i] = new ManualResetEvent(false);
ThreadPool.QueueUserWorkItem(new WaitCallback(delegate(object index)
{
int methodIndex = (int)index;
// Execute the method
methods[methodIndex].Run();
// Tell the calling thread that we're done
resetEvents[methodIndex].Set();
}), i);
}
// Wait for all threads to execute
WaitHandle.WaitAll(resetEvents);
}
}
線程安全
在多線程的環(huán)境中,最常見(jiàn)的問(wèn)題是線程安全。.NET 2.0中,Dictionary是不安全的,我用網(wǎng)上封裝的SynchronizedDictionary,當(dāng)然,我們還可以使用折騰箱子的HashTable。在.NET 4.0中,你可以使用ConcurrentDictionary。
除了集合,在訪問(wèn)FileStream等對(duì)象的情況,你都必須注意保證線程安全,否則數(shù)據(jù)會(huì)跟實(shí)際預(yù)想不一致。
任務(wù)切分
并行計(jì)算最關(guān)鍵是如何切分任務(wù)。在上一篇文章中我已經(jīng)簡(jiǎn)略地說(shuō)明,實(shí)際的邏輯比較復(fù)雜。首先,根據(jù)要搜索的文件組和最大任務(wù)數(shù)進(jìn)行切分,在每個(gè)任務(wù)中,判斷文件夾是否為遠(yuǎn)程文件夾,如果是,則使用分布式搜索,否則本地搜索,具體參看WorkV7FindFileRunner。
在上述所有文件夾搜索任務(wù)都完成后,聚合搜索結(jié)果,再根據(jù)網(wǎng)絡(luò)/本地切分任務(wù)。對(duì)于本地文件夾,則判斷是否為同一個(gè)物理磁盤,如果是,則動(dòng)態(tài)使用并行計(jì)算以實(shí)現(xiàn)加速。具體參看WorkV7.Find()。
具體參看上一篇文章的流程圖、WorkV7FindFileRunner(第一次根據(jù)文件大小、名稱等過(guò)濾)和WorkV7FindResultRunner(根據(jù)文件屬性過(guò)濾后再根據(jù)匹配方式搜索)。
分布式的實(shí)現(xiàn)
這是本程序的最大特點(diǎn)。分布式的原理就是在各個(gè)節(jié)點(diǎn)部署一個(gè)監(jiān)聽(tīng)程序,多個(gè)節(jié)點(diǎn)組合成一個(gè)grid,在監(jiān)聽(tīng)的同時(shí),也可以作為客戶端發(fā)送請(qǐng)求。
數(shù)據(jù)的傳輸
本程序沒(méi)有使用XML作為數(shù)據(jù)載體,而是使用了自定義的格式:文件頭+數(shù)據(jù)體(如文件內(nèi)容等),文件頭包括了命令等信息。整個(gè)內(nèi)容可選壓縮算法,每個(gè)數(shù)據(jù)包在最后自動(dòng)添加結(jié)束標(biāo)記,以便在TCP中識(shí)別。
協(xié)議
分布式文件快速搜索自定義了協(xié)議接口,內(nèi)置了對(duì)HTTP、TCP和FTP的支持,你可以自由添加各種協(xié)議。每種網(wǎng)絡(luò)連接,都會(huì)使用異步處理,避免堵塞請(qǐng)求。
譬如:
c:\temp\abc.txt
\\lancomputer\temp\abc.txtt
http://1.2.3.4:88/foo/abc.txt
tcp://1.2.3.4:55/foo/abc.txt
ftp://1.2.3.4/foo/abc.txt
pop3://1.2.3.4:101/foo/abc.txt
skydrive://foo/abc.txt
dropbox://foo/abc.txt
AmazonS3://foo/abc.txt
flicker://foo/abc.jpg
facebook://foo/abc.jpg
-
HTTP
每個(gè)數(shù)據(jù)包都使用POST方式,在可選壓縮后把數(shù)據(jù)寫入Request.GetRequestStream,具體參看HTTPFileWorkProvider。在服務(wù)器端,使用HttpListener監(jiān)聽(tīng),在獲得每個(gè)HttpListenerRequest后,調(diào)用基類(BaseManager)的ProcessRequest處理Request.InputStream,具體參看WorkV7HTTPManager
HttpListener有點(diǎn)詭異,使用fooListener.Prefixes.Add()來(lái)定義監(jiān)聽(tīng)的路徑。在調(diào)用HttpListener之前,你需要使用HttpListener.IsSupported判斷一下你的操作系統(tǒng)是否支持:必須XP SP2或以上、Win2003、Vista、08、Win7。HttpListener本身不支持SSL,但你可以httpcfg.exe來(lái)配置,之前我參看的是一篇英文的文章,現(xiàn)在一下子找不到,大家就湊合用中文的吧:配置HttpListener偵聽(tīng)SSL連接詳解。
-
TCP
每個(gè)具體的操作,會(huì)先使用AsynchronousClient進(jìn)行連接,服務(wù)器使用AsynchronousSocketListener進(jìn)行監(jiān)聽(tīng),在Received事件處理客戶端發(fā)送來(lái)的請(qǐng)求,具體參看TCPFileWorkProvider和WorkV7TCPManager。
-
FTP
使用.NET內(nèi)置的FTPWebRequest,具體參看FTPFileWorkProvider。
大數(shù)據(jù)的傳輸
在下一個(gè)大版本中,將會(huì)提供對(duì)文件同步的支持。文件傳輸,有很多現(xiàn)成的軟件可以做參考。我的設(shè)想是:把文件動(dòng)態(tài)切分成多個(gè)小塊以減少內(nèi)存的占用,標(biāo)記之,成功就記錄一個(gè),失敗/斷開(kāi)后下次傳輸,則可以斷點(diǎn)續(xù)傳。當(dāng)然,一樣可選壓縮算法,以提高傳輸性能。
代碼下載
點(diǎn)擊這里下載:Filio.zip
項(xiàng)目地址
本項(xiàng)目已經(jīng)在http://filio.codeplex.com/ 開(kāi)源

浙公網(wǎng)安備 33010602011771號(hào)