标签:
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(); }
由此我们可以控制 是否从外部配置文件启动。
标签:
原文地址:http://www.cnblogs.com/Daniel-Liang/p/5758348.html