码迷,mamicode.com
首页 > 其他好文 > 详细

Scut 的通用配置管理器

时间:2016-08-11 00:44:06      阅读:467      评论:0      收藏:0      [点我收藏+]

标签:

1. 配置节 ConfigSection

  private List<ConfigNode> _configNodes;

    public class ConfigNode
    {
        public ConfigNode()
        {

        }

        public ConfigNode(string key, string value)
        {
            Key = key;
            Value = value;
        }

        public string Key { get; set; }
        public string Value { get; set; }
    }

  可知,使用一个 key-value 型的链表结构来管理 基础配置。

  ConSection 里包含了对链表的操作:

public class ConfigSection
{
    private List<ConfigNode> _configNodes;   //配置域管理一个配置链表
    public int NodeCount { get { return _configNodes == null ? 0 : _configNodes.Count; } }

    public void AddNode(ConfigNode node);    //加载一个配置节点
    public ConfigNode GetNode(string key);   //获取“key”对应的配置节点
    public bool SetValue(string key, string value);  //为“key”对应的节点设置“value”
    public bool RemoveKey(string key);            //删除“key”对应的配置节点
    public void Load(string nodeString);          //批量加载“key=value;key=value”格式的配置节点
}

  

  连接配置节:最重要的是增加了对“目标连接”的管理。

public class ConnectionSection : ConfigSection
{
    public string Name { get; set; }
    public string ProviderName { get; set; }
    public string ConnectionString { get; set; }

    public ConnectionSection(string name, string providerName, string connectionString)
    {
        Load(name, providerName, connectionString);
    }

    public void Load(string name, string providerName, string connectionString)
    {
        Name = name;
        ProviderName = providerName;
        ConnectionString = connectionString;
    }
}

 

2.  配置的抽象

public interface IConfigger : IDisposable
{
     void Install();       //加载配置
     void Reload();        //重载配置
     T GetFirstConfig<T>() where T : ConfigSection;               //获取首个配置
     T GetFirstOrAddConfig<T>() where T : ConfigSection, new();   //获取首个配置, 如果没有则初始化
     IList<ConfigSection> GetAllConfig();  
     T GetConnetion<T>(string name) where T : ConnectionSection;    
}

       

  数据配置:首先数据配置域管理了配置节的集合

private readonly List<ConfigSection> _dataList = new List<ConfigSection>();

public IList<T> GetConfig<T>() where T : ConfigSection
{
    lock (_dataList)
    {
        return _dataList.OfType<T>().ToList();    //在集合中筛选出指定类型的集合
    }
}

/* 针对 _dataList 的增删改查 */
protected virtual void DoClearData();    
public IList<ConfigSection> GetAllConfig();
protected void AddNodeData(ConfigSection nodeData);
protected virtual void DoClearData()

  下面来看更重要的几个变量与功能:

private FileSystemWatcher _watcher;
private HashSet<string> _changedFiles = new HashSet<string>();
private Timer _excuteTimer;
        protected virtual void InitDependenceFile()
        {
            _excuteTimer = new Timer(OnExcute, null, Timeout.Infinite, Timeout.Infinite);
            string path = Path.GetDirectoryName(ConfigFile) ?? "";
            if (!Directory.Exists(path))
            {
                return;
            }
            string file = Path.GetFileName(ConfigFile) ?? "*.config";
            _watcher = new FileSystemWatcher(path, file);              //对配置文件的目录与文件进行监控
            _watcher.Changed += new FileSystemEventHandler(OnWatcherChanged);    //文件创建、修改、删除都会触发 “文件修改事件”
            _watcher.Created += new FileSystemEventHandler(OnWatcherChanged);
            _watcher.Deleted += new FileSystemEventHandler(OnWatcherChanged);
            _watcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.LastAccess | NotifyFilters.LastWrite | NotifyFilters.FileName | NotifyFilters.DirectoryName | NotifyFilters.Size;
            _watcher.IncludeSubdirectories = false;                  //是否级联监视指定路径的子目录--否
            _watcher.EnableRaisingEvents = true;                    //是否开启监控--是
            IsDependenced = true;
        }

  开启配置文件监控,注册监控事件,如果配置文件发生改变,则触发事件。

 

        public void Install()
        {
            if (!string.IsNullOrEmpty(ConfigFile) && File.Exists(ConfigFile))
            {
                InitDependenceFile();
            }
            LoadConfigData();
        }

  启用该配置,则先开启配置文件监控,之后再装订配置数据。

 

  那么我们要关注的则是如何文件发生改变后做了什么事。

        private void OnWatcherChanged(object sender, FileSystemEventArgs e)
        {
            try
            {
                _changedFiles.Add(e.FullPath);
                _excuteTimer.Change(_dueChangeTime, Timeout.Infinite);    //过 _dueChangeTime 这段时间后重新启动定时器
            }
            catch (Exception ex)
            {
                TraceLog.WriteError("XmlDataConfigger changed error:{0}", ex);
            }
        }

  定时器的定时处理函数为:

        private void OnExcute(object state)
        {
            try
            {
                //Repetitive loading process
                var tempFile = Interlocked.Exchange(ref _changedFiles, new HashSet<string>());    //以原子操作的形式将 _changedFiles 的值清空并返回 _changedFiles  的原始值

                foreach (var fileName in tempFile)
                {
                    var e = new ConfigChangedEventArgs() { FileName = fileName };
                    ConfigManager.OnConfigChanged(this, e);    //由总配置管理器来发布该订阅--“该文件已发生改变”   
                    Reload();
                    break;

                }
                //stop timer
                _excuteTimer.Change(Timeout.Infinite, Timeout.Infinite);
            }
            catch (Exception ex)
            {
                TraceLog.WriteError("XmlDataConfigger excute error:{0}", ex);
            }
        }
        public void Reload()
        {
            lock (_dataList)
            {
                DoClearData();
                LoadConfigData();    //重新装订配置
            }
            TraceLog.WriteLine("{0} The configger has reloaded.", DateTime.Now.ToString("HH:mm:ss"));
            var e = new ConfigReloadedEventArgs();
            ConfigManager.OnConfigReloaded(this, e);    //由总配置管理器来发布订阅--“该文件已重新加载”
        }

  

  线索都集中到了 ConfigManager 的事件通知器了

ConfigManager.OnConfigChanged(this, e);
ConfigManager.OnConfigReloaded(this, e);  

  看来 ConfigManager 可以 对所管理的任意配置文件的修改 做出反馈

 

  另外还有重要的现象:

public string ConfigFile { get; set; }

  在 DataConfigger 里没有任何 API 对齐进行管理。

protected abstract void LoadConfigData();

  还有抽象接口并没有实现。

  也就意味着,DataConfigger 算是 配置抽象的基础封装, ConfigFile 与 LoadConfigData() 是留给其子类使用的,针对具体类型还会有具体的配置封装

  留下一个疑问,监控文件的为什么要用哈希表结构 HashSet<string> _changedFiles ?

  从 Scut 来看,它监控唯一的配置文件 GameServer.exe.config 就好了,但如果监控的是一系列文件(FileSystemWatcher 有这个能力),就可以通知 ConfigManager 具体是哪个文件修改了。

 

4. 配置域管理器

     private static HashSet<IConfigger> _configgerSet;         //ConfigManager 统一管理各配置
        private static IConfigger _configger;

        public static T GetConfigger<T>() where T : IConfigger  //按配置类型 来检索获取 配置
        {
            return (T)GetConfigger(typeof(T));
        }

        public static IConfigger GetConfigger(Type type)
        {
            lock (syncRoot)
            {
                foreach (IConfigger configger in _configgerSet)
                {
                    if (configger.GetType() == type)
                    {
                        _configger = configger;
                        return configger;
                    }
                }

                instance.Install();     //正如之前所分析, install 负责开启对配置文件的监控 与 该配置域的首次参数装订
                _configgerSet.Add(instance);
                _configger = instance;
                return instance;
            }
        }

  

  除此之外,还有两个事件可以让使用者对文件修改做出监控反馈:

public static event EventHandler<ConfigChangedEventArgs> ConfigChanged;
public static event EventHandler<ConfigReloadedEventArgs> ConfigReloaded;

  

  从配置管理器也可以从 “App.config” 直接启动并生效整套配置:

        public static bool Intialize(string sectionName)
        {
            lock (syncRoot)
            {
                var section = ConfigurationManager.GetSection(sectionName);   //在App.config中查到对应名称的配置
                if (section is IConfigger)
                {
                    var instance = section as IConfigger;
                    instance.Install();             //设置文件监控、首次装订参数
                    _configgerSet.Add(instance);    //纳入配置管理器的管理 
                    _configger = instance;
                    return true;
                }
                return false;
            }
        }

 

  插入一个与此关联的 Scut 的配置启动流程:

static EnvironmentSetting()     //可以同时有静态构造函数(类级别)与普通构造函数(实例级别)
        {
            bool result;
            try
            {
                result = ConfigManager.Intialize("appServerConfigger");  //先从 app.config 中尝试加载配置 “appServerConfigger”
            }
            catch (Exception)
            {
                result = false;
            }
            if (!result)    //配置文件加载失败则加载自带的默认配置
            {
                try
                {
                    ConfigManager.GetConfigger<DefaultAppConfigger>();  
                }
                catch (Exception ex)
                {
                    TraceLog.WriteError("Configger init error:{0}", ex);
                }
            }
            LoadDecodeFunc();
        }

  由此我们可以控制 是否从外部配置文件启动

  

 

Scut 的通用配置管理器

标签:

原文地址:http://www.cnblogs.com/Daniel-Liang/p/5758348.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!