Scut:通用配置管理器

Scut:通用配置管理器,第1张

Scut:通用配置管理器

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 ? : _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();
}

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


5. App.config 的使用封装 -- ConfigUtils  

public class ConfigUtils
{ public static NameValueCollection SettingsCollection
{
get
{
return ConfigurationManager.AppSettings;
}
} private ConfigUtils()
{
} public static int GetSetting(string key, int defaultValue)
{
int result = defaultValue;
try
{
object obj = SettingsCollection[key];
result = obj == null ? defaultValue : obj.ToInt();
}
catch { }
return result;
}
}

  

欢迎分享,转载请注明来源:内存溢出

原文地址: https://outofmemory.cn/zaji/588847.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-04-12
下一篇 2022-04-12

发表评论

登录后才能评论

评论列表(0条)

保存