转:C#实现简单分布式搜索--------希望有时间能好好研究一下

 

蛙蛙推荐:用c#实现一个简单的分布式搜索

[ 收藏此页] [ 打印]

【IT168知识库】
 

目标:输入一个关键字,从不同的资源库里检索出符合条件的资源条目。其中,资源库有本地硬盘上的数据,有远程web上的数据,其中前一种资源搜索由应用程序LocalSearcher来做,后一种资源的搜索由RemWebSearcher来做,而搜索的入口是一个网站DSearchWeb。DSearchWeb收到搜索请求后,分别起两个线程去调用LocalSearcher和RemWebSearcher,等它们两个的执行结果都回来后把结果组合到一起显示给客户,其中由于RemWebSearcher工作压力比较大,他和DSearchWeb不在一台机器上,它们之间靠Rmoting通信。实际应用中RemWebSearcher可以有多台来均衡搜索的压力,并且如果某台服务器搜索超时或者抛出异常,主入口程序不能崩溃。还有就是我们讲分布式搜索的结果拿到手后需要对结果进行一些排序或者敏感词过滤的操作。

其中本地搜索和远程搜索都只写了一些示例代码,本来应该用WebRequest和lucene来完善这块儿代码的,后来觉得和本文主题离的有些远就省略了先。

先来定义相关接口和公共类
 

 

     ///  
    
///  搜索结果类,为了可以用Remoting传输,标记为可序列化
    
///  

    [Serializable]
    
public   class  SearchResult
    {
        
public  SearchResult( string  rst)
        {
            result 
=  rst;
        }
        
string  result;

        
public   string  Result
        {
            
get  {  return  result; }
            
set  { result  =  value; }
        }
    }
    
///  
    
///  搜索器接口
    
///  

     public   interface  ISearcher
    {
        List
< SearchResult >  Search( string  keyword);
    }

然后准备一个本地的搜索器和远程的搜索器
 

    ///  
    
///  本地搜索器
    
///  

     public   class  LocalSearcher :ISearcher
    {
        
#region  ISearcher 成员

        
public  List < SearchResult >  Search( string  keyword)
        {
            
// 模拟用lucene搜索本地索引
            List < SearchResult >  list  =   new  List < SearchResult > ();
            list.Add(
new  SearchResult( " 秋天不会来 " ));
           
//  Thread.Sleep(1000);
            list.Add( new  SearchResult( " 风烟滚滚唱英雄 " ));
            
// Thread.Sleep(1000);
            list.Add( new  SearchResult( " 红藕香残玉簟秋 " ));
            
return  list;
        }
        
#endregion
    }

远程搜索服务器为一个控制台程序,下面是其主要类

 

     ///  
    
///  远程搜索类,为了让remoting暴露,继承MarshalByRefObject
    
///  

     public   class  RemWebSearcher : MarshalByRefObject,ISearcher
    {
        
#region  ISearcher 成员
        
public  List < SearchResult >  Search( string  keyword)
        {
            
// 模拟用webrequest来抓取网页,并找出符合条件的结果条目
            List < SearchResult >  list  =   new  List < SearchResult > ();
            list.Add(
new  SearchResult( " 我是一条小青龙,小青龙 " ));
            
// Thread.Sleep(1000);
            list.Add( new  SearchResult( " 相见时难别亦难,东风无力白花残。 " ));
            
// Thread.Sleep(1000);
            list.Add( new  SearchResult( " 为什么命名相爱,到最后还是要重来。 " ));
            
return  list;
        }
        
#endregion
    }
    
///  
    
///  启动本台搜索服务器,真实环境中远比这个要复杂
    
///  

     class  Program
    {
        
static   void  Main( string [] args)
        {
            TcpChannel channel 
=   new  TcpChannel( 8080 );
            ChannelServices.RegisterChannel(channel,
false );
            RemotingConfiguration.RegisterWellKnownServiceType(
                
typeof (RemWeb.RemWebSearcher),
                
" RemWebSearcher " , WellKnownObjectMode.Singleton);
            Console.WriteLine(
" 服务已启动,回车后停止 " );
            Console.Read();
            channel.StopListening(
null );
            ChannelServices.UnregisterChannel(channel);
            Console.WriteLine(
" 服务已停止,回车后退出  " );
            
// 这里要两个read(),一个不顶事。
            Console.Read();
            Console.Read();

        }
    }


接下来,我们做一个网站来让用户使用,我们做成ajax的
 

None.gif     < form  id ="form1"  runat ="server" >
None.gif        
< asp:ScriptManager  ID ="ScriptManager1"  runat ="server"   />
None.gif    
< div >
None.gif        
< asp:TextBox  ID ="txtKeyword"  runat ="server" > asp:TextBox >
None.gif        
< asp:Button  ID ="btnSearch"  runat ="server"  Text ="搜索"  OnClick ="btnSearch_Click"   />
None.gif        
< asp:UpdateProgress  ID ="UpdateProgress1"  runat ="server"  AssociatedUpdatePanelID ="UpdatePanel1" >
None.gif            
< ProgressTemplate >
None.gif                Please Waitdot.gif
None.gif            
ProgressTemplate >
None.gif        
asp:UpdateProgress >
None.gif    
< br  />
None.gif        
< asp:UpdatePanel  ID ="UpdatePanel1"  runat ="server"  UpdateMode ="Conditional" >
None.gif            
< ContentTemplate >
None.gif                
< asp:Repeater  ID ="rptResults"  runat ="server" >
None.gif                
< ItemTemplate >
None.gif                
< asp:Label  runat ="server"  ID ="Label1"  Text ='<%#  Eval("Result") % > ' /> < br />
None.gif                
ItemTemplate >
None.gif                
asp:Repeater >
None.gif            
ContentTemplate >
None.gif            
< Triggers >
None.gif                
< asp:AsyncPostBackTrigger  ControlID ="btnSearch"  EventName ="Click"   />
None.gif            
Triggers >
None.gif        
asp:UpdatePanel >
None.gif    
div >
None.gif    
form >
None.gif

还有,我们要连接远程的搜索服务器

     ///  
    
///  在应用程序启动时创建到各搜索服务器的连接,关闭是注销相关通道
    
///  

     public   class  Global : System.Web.HttpApplication
    {

        TcpChannel channel;
        
protected   void  Application_Start( object  sender, EventArgs e)
        {
            channel 
=   new  TcpChannel();
            ChannelServices.RegisterChannel(channel,
false );

        }

        
protected   void  Application_End( object  sender, EventArgs e)
        {
            channel.StopListening(
null );
            ChannelServices.UnregisterChannel(channel);
        }
    }


剩下的就是分布式搜索的逻辑了
 

    /// 
InBlock.gif    
/// 状态对象
ExpandedBlockEnd.gif    
/// 

None.gif      public   class  StateObj
    
{
InBlock.gif        
public StateObj( ISearcher searcher,
InBlock.gif            
string keyWord,
InBlock.gif            AutoResetEvent are)
        
{
InBlock.gif            Searcher 
= searcher;
InBlock.gif            KeyWord 
= keyWord;
InBlock.gif            AutoEvent 
= are;
ExpandedSubBlockEnd.gif        }

InBlock.gif        
public AutoResetEvent AutoEvent;
InBlock.gif        
public string KeyWord;
InBlock.gif        
public ISearcher Searcher;
ExpandedBlockEnd.gif    }

None.gif
None.gif    
public  partial  class  _Default : System.Web.UI.Page
    
{
InBlock.gif        
protected void Page_Load(object sender, EventArgs e)
        
{
InBlock.gif
ExpandedSubBlockEnd.gif        }

InBlock.gif        List
<SearchResult> m_list = new List<SearchResult>();   //全局搜索结果列表
InBlock.gif
        Int32 numAsyncOps = 2;                                  //异步操作的数量
InBlock.gif
        AutoResetEvent areDoSearch = new AutoResetEvent(false); //全部搜索完成的事件
InBlock.gif
        AutoResetEvent areDoFilter = new AutoResetEvent(false); //敏感此过滤完成的事件
InBlock.gif
InBlock.gif        
//处理搜索按钮的方法
InBlock.gif
        protected void btnSearch_Click(object sender, EventArgs e)
        
{
InBlock.gif            getSearchResults(txtKeyword.Text.Trim());            
InBlock.gif            rptResults.DataSource 
= m_list;
InBlock.gif            rptResults.DataBind();
ExpandedSubBlockEnd.gif        }

InBlock.gif
        
/// 
InBlock.gif        
/// 进行多线程和分布式搜索
InBlock.gif        
/// 

ExpandedSubBlockEnd.gif        
/// 

InBlock.gif        private void getSearchResults(string keyWord)
        
{
InBlock.gif            
//注册全部搜索完成后将执行的回调,回调里将结果进行敏感词过滤
InBlock.gif            
//这里要设置一个合理的超时值,以防止有些服务器操作超时造成整体无响应
InBlock.gif
            RegisteredWaitHandle rwh = ThreadPool.RegisterWaitForSingleObject(
InBlock.gif                areDoSearch,
InBlock.gif                doFiterResult,
InBlock.gif                
null,
InBlock.gif                
10000,
InBlock.gif                
false);
InBlock.gif
            
创建搜索服务器列表
InBlock.gif
InBlock.gif            numAsyncOps 
= arrSearcher.Length; //重置异步操作数量
InBlock.gif

            
调用线程池线程分别执行每个远程异步搜索
InBlock.gif
            
等待每一个异步搜索直到完成或者超时
InBlock.gif
InBlock.gif            areDoFilter.WaitOne();  
//等待敏感词过滤完毕
InBlock.gif
            rwh.Unregister(null);   //注销areDoSearch事件
ExpandedSubBlockEnd.gif
        }

InBlock.gif
        
/// 
InBlock.gif        
/// 创建搜索服务器列表,真实情况下建议使用工厂方法模式
InBlock.gif        
/// 

ExpandedSubBlockEnd.gif        
/// 

InBlock.gif        private ISearcher[] getSearcherList()
        
{
InBlock.gif            List
<ISearcher> list= new List<ISearcher>();
InBlock.gif            list.Add(
new LocalSearcher());
InBlock.gif            
InBlock.gif            ISearcher searcher 
= (ISearcher)Activator.GetObject(typeof(ISearcher),
InBlock.gif                
"tcp://localhost:8080/RemWebSearcher");
InBlock.gif            list.Add(searcher);
InBlock.gif            
return list.ToArray();
ExpandedSubBlockEnd.gif        }

InBlock.gif
        
/// 
InBlock.gif        
/// 进行结果过滤操作,真实情况下需要进行关联度分析后排序,以及敏感词过滤等操作
InBlock.gif        
/// 

InBlock.gif        
/// 
ExpandedSubBlockEnd.gif        
/// 

InBlock.gif        void doFiterResult(Object state, bool timeOut)
        
{
InBlock.gif            Trace.Warn(
"{" + Thread.CurrentThread.GetHashCode().ToString() + "}:doFiterResultdot.gif");
InBlock.gif            
if (timeOut)
            
{
InBlock.gif                Trace.Warn(
"搜索超时,某些搜索服务器可能没有在指定的时间内返回结果");
ExpandedSubBlockEnd.gif            }

InBlock.gif            
lock (((ICollection)m_list).SyncRoot)
            
{
InBlock.gif                
//这里模拟一些敏感词过滤的逻辑
InBlock.gif
                m_list.Add(new SearchResult("嘿嘿一定是你吧"));
ExpandedSubBlockEnd.gif            }

InBlock.gif            areDoFilter.Set();  
//设置敏感词过滤事件为终止,通知主线程搜索结果已经可用,可以展示给用户
ExpandedSubBlockEnd.gif
        }

InBlock.gif
        
/// 
InBlock.gif        
/// 每个搜索器的执行方法
InBlock.gif        
/// 

ExpandedSubBlockEnd.gif        
/// 

InBlock.gif        void doSearch(Object state)
        
{
InBlock.gif            Trace.Warn(
"{" + Thread.CurrentThread.GetHashCode().ToString() + "}:doSearchdot.gif");
InBlock.gif
            
处理该线程上的未处理异常,虽然对搜索过程加了cathc,但还是要防止漏网之鱼
InBlock.gif
InBlock.gif
InBlock.gif            StateObj stateObj 
= (StateObj)state;
InBlock.gif
InBlock.gif            ISearcher searcher 
= stateObj.Searcher; //当前的搜索器
InBlock.gif
            string keyWord = stateObj.KeyWord;      //搜索的关键词
InBlock.gif

            
执行搜索器的搜索方法获取搜索结果,并捕获异常
InBlock.gif
InBlock.gif
            
拿到结果后,把结果合并到全局搜索结果列表里
InBlock.gif
InBlock.gif            stateObj.AutoEvent.Set();  
//通知主线程,本搜索器执行完毕,可以等下一个了。
ExpandedSubBlockEnd.gif
        }

InBlock.gif
ExpandedBlockEnd.gif    }

None.gif
None.gif

相关链接:
NET多线程同步和互斥机制初探
http://www.cnblogs.com/herony420/articles/221523.html
使用线程池
http://www.cnblogs.com/three/archive/2006/10/11/526082.html
如何对制造者线程和使用者线程进行同步
http://www.cnblogs.com/three/archive/2006/09/29/518519.html
如何:创建和终止线程
http://www.cnblogs.com/three/archive/2006/09/30/519034.html
HOW TO:使用 Visual C# .NET 同步对多线程环境中共享资源的访问
http://support.microsoft.com/kb/816161/zh-cn
@@@@C#线程池的并发问题,高手进@@@@
http://www.ttscj.cn/info/60172.htm
Visual C#中的多线程编程
http://www.manbu.net/article.asp?id=39
WaitHandle.WaitOne Method (TimeSpan, Boolean)
http://msdn2.microsoft.com/en-us/library/85bbbxt9.aspx
Microsoft .Net Remoting系列专题之一:.Net Remoting基础篇
http://www.cnblogs.com/wayfarer/archive/2004/07/30/28723.html

第一次写多线程的帖子,也是初学,可能有好多考虑不全的地方,还请各位老大指正。
相关图片和代码我就贴了。
刚开始贴的代码有些问题,不好意思。

源码下载地址如下,大家自己下载调试吧。

http://www.cnblogs.com/Files/onlytiancai/DSearch.zip

 

 

 

你可能感兴趣的:(转:C#实现简单分布式搜索--------希望有时间能好好研究一下)