[转载]分布式文件快速搜索-技术细节分析(开源/并行) – 灵感之源 – 博客园.
系列文章
2.分布式文件快速搜索的设计与实现(开源/分布式计算/并行)
3.分布式文件快速搜索-技术细节分析(开源/并行)
前言
在上一篇文章中,对分布式文件快速搜索的设计与实现进行了说明。今天,将对具体的实现细节进行分析。
文件的检索
-
文件获取
一般地,用Directory.GetDirectories()加上SearchOption.AllDirectories来获取某个的目录下 的所有文件(包括任意层子文件)。但在这里会采用自行递归获取,每获取一层目录的文件,都会预先根据SearchTypes条件(如文件大小、文件名、修 改时间、属性等)来碰撞。具体参看WorkV6FindFileRunner.FindLocal方法。
-
哈希获取
在使用SearchTypes条件过滤完成后,使用MD5CryptoServiceProvider来获取MD5哈希值。当然,你可以使用 SHA1CryptoServiceProvider计算哈希值,如果你觉得MD5不可靠,具体参看WorkUtils.HashMD5File。
实际的哈希获取,会使用缓存。缓存的实现使用快速二进制序列化,原理是判断缓存数据库是否存在一样的文件信息(文件名、大小和修改时间),如果匹配,则返回已经存在的哈希值,否则就获取新的哈希值。具体参看LocalHashStorage。
文件的匹配/比较
文件的匹配有3种方式:完全一致、包含以及全文索引。
-
完全一致
完全一致,直接使用哈希值比较。
-
包含
适用于运行时搜索,判断文本文件中包含的内容。目前直接使用File.ReadAllText(file).IndexOf(keyword, StringComparison.InvariantCultureIgnoreCase)来判断,可能遇到的问题应该是文件太大导致内存溢出。
-
全文索引
可索引文件的全文内容会自动缓存,支持自定义扩展接口IFileContentIndex,目前内置了微软的IFilter实现。具体参看LocalFileContentIndexStorage。
并行计算的处理
实现
因为不是.NET3.5/4,没有PPL,只能模拟并行,来源参看分布式文件快速搜索V6.6(多计算机并行/多种算法)。 原理是使用ThreadPool.QueueUserWorkItem各个任务,使用ManualResetEvent记住每个任务的状态,并用 WaitHandle.WaitAll等待所有任务完成。具体参看ParallelProcessor.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);
}
}
线程安全
在多线程的环境中,最常见的问题是线程安全。.NET 2.0中,Dictionary是不安全的,我用网上封装的SynchronizedDictionary,当然,我们还可以使用折腾箱子的 HashTable。在.NET 4.0中,你可以使用ConcurrentDictionary。
除了集合,在访问FileStream等对象的情况,你都必须注意保证线程安全,否则数据会跟实际预想不一致。
任务切分
并行计算最关键是如何切分任务。在上一篇文章中我已经简略地说明,实际的逻辑比较复杂。首先,根据要搜索的文件组和最大任务数进行切分,在每个任务 中,判断文件夹是否为远程文件夹,如果是,则使用分布式搜索,否则本地搜索,具体参看WorkV6FindFileRunner。
在上述所有文件夹搜索任务都完成后,聚合搜索结果,再根据网络/本地切分任务。对于本地文件夹,则判断是否为同一个物理磁盘,如果是,则动态使用并行计算以实现加速。具体参看WorkV6.Find()。
具体参看上一篇文章的流程图、WorkV6FindFileRunner(第一次根据文件大小、名称等过滤)和WorkV6FindResultRunner(根据文件属性过滤后再根据匹配方式搜索)。
分布式的实现
这是本程序的最大特点。分布式的原理就是在各个节点部署一个监听程序,多个节点组合成一个grid,在监听的同时,也可以作为客户端发送请求。
数据的传输
本程序没有使用XML作为数据载体,而是使用了自定义的格式:文件头+数据体(如文件内容等),文件头包括了命令等信息。整个内容可选压缩算法,每个数据包在最后自动添加结束标记,以便在TCP中识别。
协议
分布式文件快速搜索实现了对HTTP和TCP的支持。客户端优先尝试使用HTTP连接,在失败后,再使用TCP连接。每种网络连接,都会使用异步处理,避免堵塞请求。
-
HTTP
每个数据包都使用POST方式,在可选压缩后把数据写入Request.GetRequestStream,具体参看 WorkUtils.SendHTTPRequest。在服务器端,使用HttpListener监听,在获得每个 HttpListenerRequest后,调用基类(BaseManager)的ProcessRequest处理 Request.InputStream,具体参看WorkV6HTTPManager
HttpListener有点诡异,使用fooListener.Prefixes.Add()来定义监听的路径。在调用HttpListener 之前,你需要使用HttpListener.IsSupported判断一下你的操作系统是否支持:必须XP SP2或以上、Win2003、Vista、08、Win7。HttpListener本身不支持SSL,但你可以httpcfg.exe来配置,之前我 参看的是一篇英文的文章,现在一下子找不到,大家就凑合用中文的吧:配置HttpListener侦听SSL连接详解。
-
TCP
每个具体的操作,会先使用AsynchronousClient进行连接,服务器使用AsynchronousSocketListener进行监听,在Received事件处理客户端发送来的请求,具体参看WorkV6TCPManager。
大数据的传输
在下一个大版本中,将会提供对文件同步的支持。文件传输,有很多现成的软件可以做参考。我的设想是:把文件动态切分成多个小块以减少内存的占用,标记之,成功就记录一个,失败/断开后下次传输,则可以断点续传。当然,一样可选压缩算法,以提高传输性能。
代码下载
点击这里下载:Filio.zip
项目地址
本项目已经在http://filio.codeplex.com/ 开源