代码改变世界

分布式文件快速搜索-技术细节分析(开源/并行)

2010-07-26 18:37 by 灵感之源, ... 阅读, ... 评论, 收藏, 编辑

 

系列文章

1.分布式文件快速搜索(多计算机并行/多种算法)

2.分布式文件快速搜索的设计与实现(开源/分布式计算/并行)

3.分布式文件快速搜索-技术细节分析(开源/并行)

 

 

 

前言

 在上一篇文章中,对分布式文件快速搜索的设计与实现进行了说明。今天,将对具体的实现细节进行分析。

 

文件的检索

 

  • 文件获取

    1). 一般地,用Directory.GetDirectories()加上SearchOption.AllDirectories来获取某个的目录下的所有文件(包括任意层子文件)。但在这里会采用自行递归获取,每获取一层目录的文件,都会预先根据SearchTypes条件(如文件大小、文件名、修改时间、属性等)来碰撞。具体参看WorkV7FindFileRunner.FindLocal方法。

    2). 相比递归获取文件列表,我们可以直接读取物理磁盘的MFT(Master File Table)主文件表,这样不需要逐个目录递归,直接顺序获取所有文件夹和文件记录,速度可以比递归快10倍以上。在Windows NT便引入的NTFS文件系统,拥有USN Journal(文件日志),每个文件的变化(添加、修改、删除、更名等)都会被记录,在通过MFT获取所有文件后,每次运行,只需要判断一下系统的变化并记录便可,无需重新扫描整个磁盘。

 

  • 哈希获取

    在使用SearchTypes条件过滤完成后,使用MD5CryptoServiceProvider来获取MD5哈希值。当然,你可以使用SHA1CryptoServiceProvider计算哈希值,如果你觉得MD5不可靠,具体参看WorkUtils.HashMD5File。


    实际的哈希获取,会使用缓存。缓存的实现使用快速二进制序列化,原理是判断缓存数据库是否存在一样的文件信息(文件名、大小和修改时间),如果匹配,则返回已经存在的哈希值,否则就获取新的哈希值。具体参看LocalHashStorage。

 

文件的匹配/比较

 文件的匹配有3种方式:完全一致、包含以及全文索引。

  1. 完全一致

    完全一致,直接使用哈希值比较。

     

  2. 包含

    适用于运行时搜索,判断文本文件中包含的内容。目前直接使用File.ReadAllText(file).IndexOf(keyword, StringComparison.InvariantCultureIgnoreCase)来判断,可能遇到的问题应该是文件太大导致内存溢出。

     

  3. 全文索引

    可索引文件的全文内容会自动缓存,支持自定义扩展接口IFileContentIndex,目前内置了微软的IFilter实现。具体参看LocalFileContentIndexStorage。

 

并行计算的处理

 

实现

因为不是.NET3.5/4,没有PPL,只能模拟并行,来源参看分布式文件快速搜索v7.0(多计算机并行/多种算法)。原理是使用ThreadPool.QueueUserWorkItem各个任务,使用ManualResetEvent记住每个任务的状态,并用WaitHandle.WaitAll等待所有任务完成。具体参看ParallelProcessor.ExecuteParallel。

 

 

ExecuteParallel
public static void ExecuteParallel(IParallelWorker[] methods)
        {
            
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等对象的情况,你都必须注意保证线程安全,否则数据会跟实际预想不一致。

 

任务切分

并行计算最关键是如何切分任务。在上一篇文章中我已经简略地说明,实际的逻辑比较复杂。首先,根据要搜索的文件组和最大任务数进行切分,在每个任务中,判断文件夹是否为远程文件夹,如果是,则使用分布式搜索,否则本地搜索,具体参看WorkV7FindFileRunner。

 

在上述所有文件夹搜索任务都完成后,聚合搜索结果,再根据网络/本地切分任务。对于本地文件夹,则判断是否为同一个物理磁盘,如果是,则动态使用并行计算以实现加速。具体参看WorkV7.Find()。

 

具体参看上一篇文章的流程图、WorkV7FindFileRunner(第一次根据文件大小、名称等过滤)和WorkV7FindResultRunner(根据文件属性过滤后再根据匹配方式搜索)。

 

 

分布式的实现

这是本程序的最大特点。分布式的原理就是在各个节点部署一个监听程序,多个节点组合成一个grid,在监听的同时,也可以作为客户端发送请求。

 

数据的传输

本程序没有使用XML作为数据载体,而是使用了自定义的格式:文件头+数据体(如文件内容等),文件头包括了命令等信息。整个内容可选压缩算法,每个数据包在最后自动添加结束标记,以便在TCP中识别。

 

协议

分布式文件快速搜索自定义了协议接口,内置了对HTTP、TCP和FTP的支持,你可以自由添加各种协议。每种网络连接,都会使用异步处理,避免堵塞请求。

 

譬如:

 

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

 

  1. HTTP

    每个数据包都使用POST方式,在可选压缩后把数据写入Request.GetRequestStream,具体参看HTTPFileWorkProvider。在服务器端,使用HttpListener监听,在获得每个HttpListenerRequest后,调用基类(BaseManager)的ProcessRequest处理Request.InputStream,具体参看WorkV7HTTPManager

     

    HttpListener有点诡异,使用fooListener.Prefixes.Add()来定义监听的路径。在调用HttpListener之前,你需要使用HttpListener.IsSupported判断一下你的操作系统是否支持:必须XP SP2或以上、Win2003、Vista、08、Win7。HttpListener本身不支持SSL,但你可以httpcfg.exe来配置,之前我参看的是一篇英文的文章,现在一下子找不到,大家就凑合用中文的吧:配置HttpListener侦听SSL连接详解

     

  2. TCP

    每个具体的操作,会先使用AsynchronousClient进行连接,服务器使用AsynchronousSocketListener进行监听,在Received事件处理客户端发送来的请求,具体参看TCPFileWorkProvider和WorkV7TCPManager。

     

  3. FTP

    使用.NET内置的FTPWebRequest,具体参看FTPFileWorkProvider。

 

大数据的传输

在下一个大版本中,将会提供对文件同步的支持。文件传输,有很多现成的软件可以做参考。我的设想是:把文件动态切分成多个小块以减少内存的占用,标记之,成功就记录一个,失败/断开后下次传输,则可以断点续传。当然,一样可选压缩算法,以提高传输性能。

 

 

代码下载

点击这里下载:Filio.zip

 

 

项目地址

本项目已经在http://filio.codeplex.com/ 开源