好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

设计个性化、灵活、实时更新的配置管理器

设计个性化、灵活、实时更新的配置管理器

这里所说的个性化、灵活、实时更新的定义?

个性化 ,是指你可以随意定义自己想要的配置结构、保存格式、存放位置等等。

灵活 ,是指可以方便的对配置进行读、写操作,并可以很容易实现任意多个配置管理器。

实时更新 ,是指在配置发生改变时可以实时的更新,且不会重启Web应用程序。

下面开始讲解设计。既然是配置管理器,那还是先定义好接口吧,请看 IFileConfigManager<T> :

?

/// <summary>

/// Interface containing all properties and methods to be implemented

/// by file configuration manager.

/// </summary>

/// <typeparam name="T">The type of config entity.</typeparam>

public interface IFileConfigManager<T> : IDisposable

     where T : class , new ()

{

     /// <summary>

     /// Gets the path of the config file.

     /// </summary>

     string Path { get ; }

 

     /// <summary>

     /// Gets the encoding to read or write the config file.

     /// </summary>

     Encoding Encoding { get ; }

 

     /// <summary>

     /// Gets the serializer of the config manager for loading or saving the config file.

     /// </summary>

     FileConfigSerializer<T> Serializer { get ; }

 

     /// <summary>

     /// Gets the current config entity.

     /// </summary>

     /// <returns></returns>

     T GetConfig();

 

     /// <summary>

     /// Saves the current config entity to file.

     /// </summary>

     void SaveConfig();

 

     /// <summary>

     /// Saves a specified config entity to file.

     /// </summary>

     /// <param name="config"></param>

     void SaveConfig(T config);

 

     /// <summary>

     /// Backups the current config entity to a specified path.

     /// </summary>

     /// <param name="backupPath"></param>

     void BackupConfig( string backupPath);

 

     /// <summary>

     /// Restores config entity from a specified path and saves to the current path.

     /// </summary>

     /// <param name="restorePath"></param>

     void RestoreConfig( string restorePath);

}

T参数当然就是定义的配置类型了,而且必须是引用类型,有无参数构造函数。Path是配置文件的完整路径,Encoding是读取和保存配置时用的编码,Serializer是处理配置序列化和反序列化的具体实现,GetConfig()是获取当前配置,SaveConfig()是保存当前配置,SaveConfig(T config)是保存指定的配置,BackupConfig(string backupPath)备份配置到指定路径,RestoreConfig(string restorePath)从指定路径还原配置。

接口IFileConfigManager<T>中定义的Serializer是用于支持自定义配置序列化功能的,下面看看 FileConfigSerializer<T> 的实现:

?

public abstract class FileConfigSerializer<T>

     where T : class , new ()

{

     #region Fields

 

     // XML格式

     public static readonly FileConfigSerializer<T> Xml = new XmlFileConfigSerializer();

 

     // 二进制格式

     public static readonly FileConfigSerializer<T> Binary = new BinaryFileConfigSerializer();

 

     #endregion

 

     #region Methods

 

     // 从配置文件反序列化,使用指定的编码

     public abstract T DeserializeFromFile( string path, Encoding encoding);

 

     // 序列化到配置文件,使用指定的编码

     public abstract void SerializeToFile(T config, string path, Encoding encoding);

 

     #endregion

 

     #region XmlFileConfigSerializer

 

     // 实现默认的Xml序列化类

     private sealed class XmlFileConfigSerializer : FileConfigSerializer<T>

     {

         public override T DeserializeFromFile( string path, Encoding encoding)

         {

             return SerializationUtil.DeserializeFromXmlFile<T>(path, encoding);

         }

 

         public override void SerializeToFile(T config, string path, Encoding encoding)

         {

             SerializationUtil.SerializeToXmlFile(config, path, encoding);

         }

     }

 

     #endregion

 

     #region BinaryFileConfigSerializer

 

     // 实现默认的二进制序列化类

     private sealed class BinaryFileConfigSerializer : FileConfigSerializer<T>

     {

         public override T DeserializeFromFile( string path, Encoding encoding)

         {

             return SerializationUtil.DeserializeFromBinaryFile<T>(path, encoding);

         }

 

         public override void SerializeToFile(T config, string path, Encoding encoding)

         {

             SerializationUtil.SerializeToBinaryFile(config, path, encoding);

         }

     }

 

     #endregion

}

 FileConfigSerializer<T>定义为抽象类,是为了方便默认的使用和扩展,里面使用的SerializationUtil类,是本人为了方便写的一个简单的序列化助手类,相信大家对对象的序列化操作不会陌生了,无非使用了System.Xml.Serialization.XmlSerializer、System.Runtime.Serialization.Formatters.Binary.BinaryFormatter、System.Runtime.Serialization.Json.DataContractJsonSerializer和System.Runtime.Serialization.NetDataContractSerializer来处理。如果不想用它们,你还可以实现FileConfigSerializer<T>进行完全的自己定义配置的加载与保存方式。对于json序列化推荐大家使用 http://www.codeplex.com/json/  。

好了,大家已经知道了接口的定义了,下面来讲讲实时更新配置功能有哪些方法可以实现。我们知道,如果利用Web.config来配置的话,第一:如果配置内容多而杂,那会很乱;第二:如果手动修改配置,会导致Web重启(而我们并不希望它重启),所以,如果要解决上面两点问题,我们就要思考点什么了。上面我提到了Discuz!论坛的.net开源版本里配置管理,它是使用Timer来定时查检配置是否有修改,如果有修改就重新加载的,恩,这是一个可行的方案。还有其它方法吗?必须是有的,只要你肯去思考,下面列出本人想到的几个比较容易想到的方案:

方法1:使用Timer(.net库里有三个timer,请自行选择),每隔一秒就查检一下配置文件修改时间,如果文件被修改了,正更新最后修改时间并重新加载配置内容;

方法2:使用System.IO.FileSystemWatcher,可以实时监控配置文件,一发生改变即重新加载配置内容;

方法3:使用System.Web.Caching.Cache,加上缓存依赖,文件更改后缓存会失效,同样可以实时重新加载配置内容。

这三种方法中,方法3是本人比较推荐的,因为它的开销最小,而且可以实时更新配置,实现起来也是最简单的。对于新手可能看到这还不知道实现,下面再贴出本人实现上面接口的四个类,一个是默认管理器类,没有实时更新的功能,其它三个就是实现上面三种方法的管理器类了。

?

internal class DefaultFileConfigManager<T> : DisposableObject, IFileConfigManager<T>

     where T : class , new ()

{

     #region Fields

 

     private string path = null ;

     private Func< string > pathCreator = null ;

 

     #endregion

 

     #region Constructors

 

     public DefaultFileConfigManager(Func< string > pathCreator, FileConfigSerializer<T> serializer, Encoding encoding)

     {

         pathCreator.ThrowsIfNull( "pathCreator" );

         serializer.ThrowsIfNull( "serializer" );

 

         this .pathCreator = pathCreator;

         this .Encoding = encoding;

         this .Serializer = serializer;

         this .SyncRoot = new object ();

         this .Config = null ;

     }

 

     #endregion

 

     #region Properties

 

     public string Path

     {

         get

         {

             if ( this .path == null )

             {

                 string path = this .pathCreator();

 

                 path.ThrowsIfNull( "The path returned form pathCreator is null." );

 

                 this .path = path;

                 this .LazyInitialize();

             }

 

             return this .path;

         }

     }

 

     public Encoding Encoding

     {

         get ;

         protected set ;

     }

 

     public FileConfigSerializer<T> Serializer

     {

         get ;

         protected set ;

     }

 

     protected object SyncRoot

     {

         get ;

         set ;

     }

 

     protected virtual T Config

     {

         get ;

         set ;

     }

 

     #endregion

 

     #region Methods

 

     public virtual T GetConfig()

     {

         if ( this .Config == null )

         {

             lock ( this .SyncRoot)

             {

                 if ( this .Config == null )

                 {

                     FileInfo file = new FileInfo( this .Path);

                     if (!file.Exists)

                     {

                         // make sure the existence of the config directory

                         if (!file.Directory.Exists)

                         {

                             file.Directory.Create();

                         }

                         // save the default config to file

                         this .Config = new T();

                         this .Serializer.SerializeToFile( this .Config, this .Path, this .Encoding);

                     }

                     else

                     {

                         // else, loads from the specified path

                         this .Config = this .Serializer.DeserializeFromFile( this .Path, this .Encoding);

                     }

                 }

             }

         }

 

         return this .Config;

     }

 

     public void SaveConfig()

     {

         this .SaveConfig( this .GetConfig());

     }

 

     public virtual void SaveConfig(T config)

     {

         config.ThrowsIfNull( "config" );

 

         lock ( this .SyncRoot)

         {

             FileInfo file = new FileInfo( this .Path);

 

             // make sure the existence of the config directory

             if (!file.Directory.Exists)

             {

                 file.Directory.Create();

             }

 

             this .Config = config;

             this .Serializer.SerializeToFile( this .Config, this .Path, this .Encoding);

         }

     }

 

     public void BackupConfig( string backupPath)

     {

         backupPath.ThrowsIfNull( "backupPath" );

 

         T config = this .GetConfig();

         this .Serializer.SerializeToFile(config, backupPath, this .Encoding);

     }

 

     public void RestoreConfig( string restorePath)

     {

         restorePath.ThrowsIfNull( "restorePath" );

 

         T config = this .Serializer.DeserializeFromFile(restorePath, this .Encoding);

         this .SaveConfig(config);

     }

 

     // this method is provided to subclasses to initialize their data

     protected virtual void LazyInitialize()

     {

     }

 

     #endregion

}

?

internal sealed class FileConfigManagerWithTimer<T> : DefaultFileConfigManager<T>

     where T : class , new ()

{

     private Timer timer = null ;

     private DateTime lastWriteTime = DateTime.MinValue; // a flag to notify us of the change config

 

     public FileConfigManagerWithTimer(Func< string > pathCreator, FileConfigSerializer<T> serializer, Encoding encoding)

         : base (pathCreator, serializer, encoding)

     {

     }

 

     protected override void LazyInitialize()

     {

         base .LazyInitialize();

 

         // initializes the timer, with it's interval of 1000 milliseconds

         this .timer = new Timer(1000);

         this .timer.Enabled = true ;

         this .timer.AutoReset = true ;

         this .timer.Elapsed += new ElapsedEventHandler(Timer_Elapsed);

         this .timer.Start();

     }

 

     protected override void Dispose( bool disposing)

     {

         if (disposing)

         {

             // disposes the timer

             this .timer.Dispose();

             this .timer = null ;

         }

     }

 

     private void Timer_Elapsed( object sender, ElapsedEventArgs e)

     {

         if (!File.Exists( this .Path))

         {

             // the file has been deleted

             return ;

         }

 

         var tempWriteTime = File.GetLastWriteTime( this .Path);

 

         // if equals to the initial value, update it and return

         if ( this .lastWriteTime == DateTime.MinValue)

         {

             this .lastWriteTime = tempWriteTime;

             return ;

         }

 

         // if no equals to new write time, update it and reload config

         if ( this .lastWriteTime != tempWriteTime)

         {

             this .lastWriteTime = tempWriteTime;

 

             lock ( this .SyncRoot)

             {

                 this .Config = this .Serializer.DeserializeFromFile( this .Path, this .Encoding);

             }

         }

     }

}

?

internal sealed class FileConfigManagerWithFileWatcher<T> : DefaultFileConfigManager<T>

     where T : class , new ()

{

     private FileWatcher watcher = null ;

 

     public FileConfigManagerWithFileWatcher(Func< string > pathCreator, FileConfigSerializer<T> serializer, Encoding encoding)

         : base (pathCreator, serializer, encoding)

     {

     }

 

     protected override void LazyInitialize()

     {

         base .LazyInitialize();

 

         // when the path is created, the watcher should be initialize at the same time

         watcher = new FileWatcher( this .Path, FileChanged);

         // just start watching the file

         watcher.StartWatching();

     }

 

     protected override void Dispose( bool disposing)

     {

         if (disposing)

         {

             // disposes the watcher

             this .watcher.Dispose();

             this .watcher = null ;

         }

 

         base .Dispose(disposing);

     }

 

     private void FileChanged( object sender, FileSystemEventArgs args)

     {

         lock ( this .SyncRoot)

         {

             this .watcher.StopWatching();

             try

             {

                 // note: here making the cuurent thread sleeping a litle while to avoid exception throwed by watcher

                 Thread.Sleep(10);

                 // reload the config from file

                 this .Config = this .Serializer.DeserializeFromFile( this .Path, this .Encoding);

             }

             catch (Exception)

             {

                 // ignore it

             }

             finally

             {

                 this .watcher.StartWatching();

             }

         }

     }

}

?

internal sealed class FileConfigManagerWithCacheDependency<T> : DefaultFileConfigManager<T>

     where T : class , new ()

{

     const string KeyPrefix = "FileConfig:" ;

 

     public FileConfigManagerWithCacheDependency(Func< string > pathCreator, FileConfigSerializer<T> serializer, Encoding encoding)

         : base (pathCreator, serializer, encoding)

     {

     }

 

     protected override T Config

     {

         get

         {

             return HttpRuntime.Cache[KeyPrefix + this .Path] as T;

         }

         set

         {

             // if not null, update the cache value

             if (value != null )

             {

                 HttpRuntime.Cache.Insert(KeyPrefix + this .Path, value, new CacheDependency( this .Path), DateTime.

作者: Leo_wl

    

出处: http://www.cnblogs.com/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于设计个性化、灵活、实时更新的配置管理器的详细内容...

  阅读:32次