代码改变世界

SharedCache分析:服务端程序

2008-02-07 23:10  无常  阅读(3738)  评论(4编辑  收藏  举报

SharedCache由3个主要的项目组成MergeSystem.Indexus.WinServiceCommon、MergeSystem.Indexus.WinService和MergeSystem.Indexus.Notify。WinService可以以Windows服务方式加载,也可以以控制台方式运行,如果注册为Windows服务,则可以通过MergeSystem.Indexus.Notify程序来了解其状态,若是以控制台方式运行,则运行时的信息会在控制台窗口中显示。当然也可以配置通过NLog.dll记录日志。

大部分的功能都封装在WinServiceCommon项目中,WinService项目只负责监听和数据中转,这个项目中只有几个文件,如图1。

Indexus是个windows服务,也是程序的入口点,其类图如图2。

01

02

图1

图2

 

image

图3 ShareCache服务端程序主流程

Indexus可以接受几个命令行参数/install /i /uninstall /u来安装或卸载windows服务,若想以控制台方式启动程序,可以使用/local参数,如:
MergeSystem.Indexus.WinService.exe /local

或直接使用相同目录中的几个批处理文件来执行。来看一下main函数:

public static void Main(string[] args)
{
   
Access Log Access Log

   
string optionalArgs = string.Empty;
   
if (args.Length > 0)
   
{
        optionalArgs
= args[0];
    }

   
args Handling args Handling
}



这里可以看到,加/local参数启动时,直接调用StartService()方法,然后就是和安装windows服务后启动服务一样了。

StartService中调用ServiceLogic.Init()方法进行初始化。代码注释比较少,但已经够我们理解这段代码了。

 

/// <summary>
/// Inits this instance. This method used at startup to initialize
/// all required server components
/// </summary>

public void Init()
{
   
Access Log Access Log

    COM.Handler.LogHandler.Force(
"Initializing Settings" + COM.Enums.LogCategory.ServiceStart.ToString());
    Console.WriteLine(
@"Welcome to indeXus.Net Shared Cache");
    Console.WriteLine();
    Console.WriteLine(COM.Handler.Config.DisplayAppSettings());
    COM.Handler.LogHandler.Info(COM.Handler.Config.DisplayAppSettings());
   
// needs to be instantiated before TCP, it needs an instance of CachExpire
    cacheExpireInstance = new CacheExpire();
   
// TCP needs an instance of CacheExpire
    tcpInstance = new TcpServer(cacheExpireInstance);

    COM.Handler.LogHandler.Force(
"Init and Start Thread Tcp");
    COM.Handler.LogHandler.Force(
"Init and Start Thread CacheExpire");
   
// Init all extenders
   
// an extender is a class which initializes its own logic around
   
// specific issue within its own thread;
    ///////////////////////////////////////////////////////////////////
    this.workerTcp = new Thread(this.tcpInstance.Init);
   
this.workerTcp.Name = "TCP Handler";
   
this.workerTcp.IsBackground = true;
   
this.workerTcp.Priority = ThreadPriority.Normal;
   
///////////////////////////////////////////////////////////////////
    this.workerCacheExpire = new Thread(this.cacheExpireInstance.Init);
   
this.workerCacheExpire.Name = "Cache Expire Handler";
   
this.workerCacheExpire.IsBackground = true;
   
this.workerCacheExpire.Priority = ThreadPriority.Lowest;
   
///////////////////////////////////////////////////////////////////
    this.workerCacheExpire.Start();
   
this.workerTcp.Start();

   
// enable the search of replicaiton servers
    if (this.enableServiceFamilyMode)
   
{
        NetworkDistribution.Init();
    }
 

   
string msgThreadInfo = Environment.NewLine + 
       
"Main Thread Id: " + Thread.CurrentThread.ManagedThreadId.ToString() + Environment.NewLine +
       
"this.workerTcp: " + this.workerTcp.ManagedThreadId.ToString() + Environment.NewLine +
       
/*"this.workerTimer: " + this.workerTimer.ManagedThreadId.ToString() + Environment.NewLine +*/
       
"this.workerCacheExpire: " + this.workerCacheExpire.ManagedThreadId.ToString();

    Console.WriteLine(msgThreadInfo
+ Environment.NewLine);
    Console.WriteLine(
"+ + + + + + + + + + + + + + + + + + + + + + + + + + + + ");
    Console.WriteLine(
"server is ready to receive data.");

    COM.Handler.LogHandler.Force(
"IndeXus.Net Service Started " + COM.Enums.LogCategory.ServiceStart.ToString());
}

 

 

这里启动2个Thread,一个负责执行CacheExpire.Init()方法,负责定时轮查缓Cache中设置有过期策略的对象,如果有到期的,就从Cache中清除。另一个Thread负责监听TCP端口,然后每有一个新的客户端连接过来,又启动一个新的线程处理。

接下来转到MergeSystem.Indexus.WinService.TcpServer.Init()方法:

 

public void Init()
{
   

    IPEndPoint endpoint
= COM.Handler.Network.GetServerAnyIPEndPoint(this.cacheIpPort);

    serverSocket
= new Socket(endpoint.Address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
    serverSocket.Bind(endpoint);
    serverSocket.Listen((
int)SocketOptionName.MaxConnections);

   

   
// setup listener issues
    acceptThread = new Thread(AcceptConnections);
    acceptThread.IsBackground
= true;
    acceptThread.Priority
= ThreadPriority.Normal;
    acceptThread.Start();

   

}


 

这里启动一个新线程监听TCP 48888(默认值)端口,跟到AcceptConnections方法:

 

private void AcceptConnections()
{
   

   
while (true)
   
{
       
// Accept a connection
        Socket socket = serverSocket.Accept();
        ConnectionInfo connection
= new ConnectionInfo();
        connection.Socket
= socket;

       
// Modified:06-01-2008 Merge System GmbH, rschuetz : for more information checkout the following link> http://netrsc.blogspot.com/2007/02/another-extract-from-scalenet-threading.html
        ThreadPool.QueueUserWorkItem(this.ProcessConnection, connection);

       
// Store the socket in the open connections
        lock (this.connections)
       
{
           
this.connections.Add(connection);
        }
 

    }

}


 

 

这个比较简单,只是在一个死循环中等待客户端连接,等到后要线程池中执行ProcessConnection方法来处理,此线程继续等待下一个请求。跟到ProcessConnection方法:

 

private void ProcessConnection(object stats)
{
     

    ConnectionInfo connection
= (ConnectionInfo)stats;

   
try
   
{
       

       
// read data from Socket and convert it to an IndeXusMessage
        COM.IndexusMessage msg = new COM.Handler.NetworkMessage().ProcessNetworkMessage(connection.Socket);

       
if (msg != null)
       
{
           
this.ProcessMessage(msg, connection);
        }

       
else
       
{
            Console.WriteLine(
"Error appears due processing network message!");
            COM.Handler.LogHandler.Error(
"Error appears due processing network message!");
        }

    }

   
catch (OutOfMemoryException ex)
   
{
       
    }

   
catch (Exception ex)
   
{
       

    }

   
finally
   
{
        connection.Socket.Close();
       
lock (this.connections)
           
this.connections.Remove(connection);
    }

}


 

 

这里只调用了COM.Handler.NetworkMessage().ProcessNetworkMessage()方法将客户端发过来的内容还原为IndexusMessage 对象,然后交给ProcessMessage()方法处理:

 

private void ProcessMessage(COM.IndexusMessage msg, ConnectionInfo connection)
{
   

   
// check status first [Request || Response]
    switch (msg.Status)
   
{
       
case COM.IndexusMessage.StatusValue.Request:
           
{
               
request case request case
            }

       
case COM.IndexusMessage.StatusValue.Response:
           
{
               
            }

    }
 

}


 

 

这里分类对消息进行处理,如添加到缓存或移除等。下面仔细看下Add分支:

 

case COM.IndexusMessage.ActionValue.Add:
   
{
       
Add Case Add Case
    }


 

 

主要完成了4个功能:1.将数据保存到LocalCache中;2.如果要缓存的对象设置有过期时间,则在expire中保存一份存根;3.如果配置有复制服务器,则分发到各兄弟节点;4.如果缓存对象超出了最大值,则使用配置的策略靖仓。LocalCache是MergeSystem.Indexus.WinServiceCommon.Cache类型的静态成员,expire是WinServiceCommon.CacheExpire类型的静态成员。

分析到这里,ShartCache的大概思路已经很清晰了。

ShartCache的核心是MergeSystem.Indexus.WinServiceCommon.Cache和MergeSystem.Indexus.WinServiceCommon.CacheExpire这二个类。WinServiceCommon.Cache中使用一个字典对象readonly Dictionary<string, byte[]> dict;来存储需要缓存的数据,因为要缓存的数据都已经序列化通过TCP传输过来,这里就直接保存byte[]类型的了。WinServiceCommon.CacheExpire类中也有一个字典对象private static Dictionary<string, DateTime> expireTable = null;所有设置有过期时间的缓存对象,都在这里有个存根,过期的缓存有WinServiceCommon.CacheExpire负责清除。

 

来源:wuchang.cnblogs.com