在分布式服务站点体系中,应用程序配置往往都是随程序发布包走,在线上运营的过程中,未有自动化部署工具的情况下,靠人力来部署上百台应用服务器,非同一般。即便通过自动化工具来完成配置更新与部署,多少还是要烦劳运维人员。于是乎,我们可以通过配置项的单点化来实现:通过在DB端完成数据配置,各应用系统共享配置项,数据、配置更新依赖缓存及时刷新即可。
如有特殊需求,需要对单台服务器做配置区分,则可以通过增加APP配置的优先级来调整实现本地、DB端配置的顺序。 实现方式间接明了,下面给出示例:
1、Cache配置数据
/// <summary>
/// 获取配置表中数据并缓存
/// </summary>
/// <returns></returns>
public static List<InitialParamEntity> GetList()
{
WebLog.WriteStart("Get Parameter List...");
List<InitialParamEntity> list = null;
try
{
string key = Consts.Keys.GET_PARA_INIT_INFO;
if (HttpContext.Current == null || HttpContext.Current.Cache[key] == null)
{
list = ParameterDA.GetList();
CacheHelper.Store(key, list);
}
list = CacheHelper.GetCache<List<InitialParamEntity>>(key);
}
catch (System.Exception ex)
{
WebLog.WriteStart("Get Parameter Exception:" + ex.GetObjectDetails("|"));
}
return list;
}
/// <summary>
/// Summary description for InitialParamEntity
/// </summary>
public class InitialParamEntity
{
/// <summary>
/// 参数类型
/// </summary>
public string ParaType
{
get;
set;
}
/// <summary>
/// 参数名
/// </summary>
public string ParaKey
{
get;
set;
}
/// <summary>
/// 参数值
/// </summary>
public string ParaValue
{
get;
set;
}
}
2、缓存刷新(略)
在HttpHandler中配置缓存刷新Path,通过该Path以及对应的刷新参数,完成对应缓存或全缓存的刷新
3、配置项处理
建立配置类,继承IConfigurationSectionHandler, 在Global入口处调用Init方法完成数据初始化
public class MyConfig : IConfigurationSectionHandler
{
private MyConfig() { }
#region read appsettings
#region 不同参数类型
#region 默认配置
public static string GetConfig(string key)
{
string config = GetConfig<string>(key);
if (String.IsNullOrEmpty(config))
{
return string.Empty;
}
return config;
}
public static T GetConfig<T>(string key)
{
return GetConfig<T>(key, default(T));
}
public static T GetConfig<T>(string key,T defaultValue)
{
if (Params == null || Params.Count == 0)
return defaultValue;
string parValue = Params.Get(key);
if (!String.IsNullOrEmpty(parValue))
{
return (T)Convert.ChangeType(parValue, typeof(T));
}
return defaultValue;
}
#endregion
#region Url 跳转
/// <summary>
/// 用户获取根据类型获得URL跳转地址
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <returns></returns>
public static T GetMap<T>(string key)
{
if (MapPath == null || MapPath.Count == 0)
return default(T);
string parValue = MapPath.Get(key);
if (!String.IsNullOrEmpty(parValue))
{
return (T)Convert.ChangeType(parValue, typeof(T));
}
return default(T);
}
public static string GetMap(string key)
{
return GetMap<string>(key);
}
#endregion
#endregion
public static string GetRadomConfig(string key, string defaultValue)
{
return GetRadomConfig<string>(key, defaultValue);
}
public static T GetRadomConfig<T>(string key, T defaultValue)
{
string result = GetConfig(key);
string[] tempRet = result.Split(';');
Random random = new Random();
int index = random.Next(0, tempRet.Length);
result = tempRet[index];
return (T)Convert.ChangeType(result, typeof(T));
}
/// <summary>
/// 随机取一个值
/// </summary>
/// <returns></returns>
private static string GetRadomConfig(string value)
{
string[] serverList = value.Split(',');
if (serverList.Length == 0) throw new ArgumentException("请求参数异常");
int len = serverList.Length;
if (len > 1)
{
Random rd = new Random((int)(DateTime.Now.Ticks));
int rdNum = rd.Next(len);
return serverList[rdNum];
}
return serverList[0];
}
#endregion
#region Public attribute
private static NameValueCollection _mapPath;
public static NameValueCollection MapPath
{
get { return _mapPath; }
}
private static NameValueCollection _params;
public static NameValueCollection Params
{
get { return _params; }
}
[Config(Key = "PayOrdersConnectionString", DefaultValue = "")]
private static string payOrdersConnectionString;
/// <summary>
/// 新的订单库
/// </summary>
public static string PayOrdersConnectionString
{
get
{
return payOrdersConnectionString;
}
}
#endregion
#region internal
static void SetField(Action<FieldInfo, ConfigAttribute> action)
{
Type type = typeof(MerchantConfig);
FieldInfo[] fields = type.GetFields(BindingFlags.Static | BindingFlags.NonPublic);
foreach (FieldInfo field in fields)
{
ConfigAttribute configAttr = (ConfigAttribute)Attribute.GetCustomAttribute(field, typeof(ConfigAttribute));
if (configAttr != null)
{
action(field, configAttr);
}
}
}
static void SetParamsValue(FieldInfo field, ConfigAttribute configAttr)
{
foreach (string s in Params.AllKeys)
{
if (string.Compare(s, configAttr.Key, true) == 0)
{
field.SetValue(null, Convert.ChangeType(Params[configAttr.Key], configAttr.ValueType));
}
}
}
static void SetFieldDefault(FieldInfo field, ConfigAttribute configAttr)
{
field.SetValue(null, Convert.ChangeType(configAttr.DefaultValue, configAttr.ValueType));
}
internal static void UserDefinedCreate(NameValueCollection settings, NameValueCollection mapPath)
{
if (settings != null)
{
//Config配置,优先级高于数据库配置
NameValueCollection rewriterSettings = (NameValueCollection)System.Configuration.ConfigurationManager.GetSection("MyParamSettings");
if (rewriterSettings != null)
{
foreach (string key in rewriterSettings.Keys)
{
//!string.IsNullOrEmpty(rewriterSettings[key]), 允许空字符
if (rewriterSettings[key] != null)
{
if (settings[key] != null)
{
WebLog.WriteStart("数据库:" + key + ":" + settings[key]);
WebLog.WriteStart("Config:" + key + ":" + rewriterSettings[key]);
settings[key] = rewriterSettings[key];
}
else if (mapPath[key] != null)
{
WebLog.WriteStart("数据库:" + key + ":" + mapPath[key]);
WebLog.WriteStart("Config:" + key + ":" + rewriterSettings[key]);
mapPath[key] = rewriterSettings[key];
}
}
}
}
_mapPath = mapPath;
_params = settings;
SetField(SetParamsValue);
}
else
{
SetField(SetFieldDefault);
}
}
/// <summary>
/// 获取数据库配置数据:初始化Config,生成静态XML配置文件
/// </summary>
public static void Init(List<InitialParamEntity> list)
{
NameValueCollection mapPath = new NameValueCollection();
NameValueCollection setting = new NameValueCollection();
foreach (InitialParamEntity param in list)
{
if (param.ParaType != null && param.ParaType.ToUpper() == "MAPPATH")
{
mapPath.Add(param.ParaKey, param.ParaValue);
}
else
{
setting.Add(param.ParaKey, param.ParaValue);
}
WebLog.WriteStart(param.ParaKey + ", " + param.ParaValue);
}
UserDefinedCreate(setting, mapPath);
}
#endregion
#region IConfigurationSectionHandler 成员
public object Create(object parent, object configContext, System.Xml.XmlNode section)
{
//调用自定义的初始化函数
System.Diagnostics.Debug.WriteLine("config is modified------");
NameValueCollection settings;
try
{
System.Diagnostics.Debug.WriteLine("-------- Begin read configuration file");
NameValueSectionHandler baseHandler = new NameValueSectionHandler();
settings = (NameValueCollection)baseHandler.Create(parent, configContext, section);
}
catch
{
settings = null;
}
return settings;
}
#endregion
}
#region 默认值属性类
[AttributeUsage(AttributeTargets.Field, AllowMultiple = false, Inherited = false)]
internal class ConfigAttribute : Attribute
{
private object defaultValue;
private string key;
private Type valueType = typeof(string);
public object DefaultValue
{
get { return defaultValue; }
set { defaultValue = value; }
}
public string Key
{
get { return key; }
set { key = value; }
}
public Type ValueType
{
get { return valueType; }
set { valueType = value; }
}
}
#endregion
4、提升单台机器配置优先级
<section name="MyParamSettings" type="System.Configuration.NameValueSectionHandler,System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<MyParamSettings>
<!-- 优先级高于数据库配置项 -->
<add key="IFunctionService" value="http://www.baiduc.om" />
</MyParamSettings>
5、配置项应用
MyConfig.GetConfig("IFunctionService")
MyConfig.GetConfig<string>("IFunctionService")
MyConfig.GetConfig<int>("IFunctionService")