菜单

金沙8331何以分离web.config创新版本

2019年5月4日 - 金沙编程资讯

    公司是做CS产品的, 方今分红给本身三个活, 必要:
    一. 铺面先后运行时, 检查评定是还是不是有安顿文件, 未有的话则按私下认可值创造三个
    二. 安顿文件要加密, 无法让客户无论是看看里边的参数
    3. 配备文件要有配套的GUI配置工具, 因为现场执行人口嫌XML配置麻烦

热更新:当用户初次打开app,它会将兼具的web内容复制1份到表面存款和储蓄。此后从表面存款和储蓄加载web内容,而并不加载打包在app内部的web内容。app每一遍运行都会三番五次服务器检查更新并下载新的web内容。假如下载了履新,本次更新内容将会在下次app运维时生效。

小心:本章代码基于 第玖章
集团项目开荒–遍布式缓存Redis(二)

     
三年前作者享受了怎么着分离web.config中的配置节,因为有个别项目过大,变成N多配置节存在于web.config中,缺点如下:
     
一:不轻松处理,当你想搜索1个配置节时,看着整页的code,手足无措,为此你唯有ctrl+f来消除。
     
2:布署时也及轻松失误,铺排人士须求服从你写的配置文书档案,多个八个加,即费时又轻松失误,比方一相当的大心将其余节点给覆盖了这么。
      三:在web.config中的配置节的修改会滋生站点重启。
      四:访问配置节不够精炼,轻巧失误。

    假使唯有1个产品要求那个职能,
把各种配置项的读写功用硬编码写到工具里就到位了,
但公司有某个个产品都急需那一个, 不得不写2个通用的工具类

安装cordova

亟需设置cordova五.0+

cordova plugin add cordova-hot-code-push-plugin

此命令下会方便生成必需的app配置文件
起步当地服务器,监听开荒情势下的web内容退换,并直接配置新本子。

代码的github地址:

     
小说从前自身关系过我们为了消除此种难点的解决方案,正是将配置节从web.config文件中分离出来,将配备节存入单独的公文中。具体的方案请参考前边的文章(怎样划分web.config
),有不长一段时间未有采纳了,近来在选择上开采在多类型中复用有早晚难点,即每种体系都亟需编写制定一段不够长的代码,上面就是自身对于它的优化进度,先看下原来的专业量:

    这一个专门的工作还化解了三个问题:
    a. 以前设置项都配置在 app.config 里, 每趟进级都会覆盖原来的装置,
所以现场职员都无法不先将 app.config复制出来.
    b. app.config 里新扩展了安插项, 现场实行人口必须仔细相比较,
将激增项人工放入原来的app.config

Cordova项目急忙指引

一、创建新的科尔多瓦项目,并加多android和ios platform;

cordova create TestProject com.won.testproject TestProject

cordova platform add android

二、增添插件:

cordova plugin add cordova-hot-code-push-plugin

三、增多开拓扩展

cordova plugin add cordova-hot-code-push-local-dev-addon

四、安装Cordova Hot Code Push命令行客户端

npm install -g cordova-hot-code-push-cli

伍、运维当地服务器

cordova-hcp server

陆、张开新的调整台,进入到品种根目录运维app

cordova run android

 

     
第2:Webconfig,那是框架之中的原委,那也是不二法门能够复用的地方。它是二个进口,全体的配备文件引用都经过它,举个例子访问酒店的布署类,WebConfig.Hotel.HotelName。代码如下:    

    未来的做法是, 配置文件ConfigSetting.xml并不在安装包中,
所以卸载晋级都不会潜移默化它; 程序第二回运维时,
会按私下认可值生成多少个ConfigSetting.xml; 将来先后运行的时候,
假设有新增添的布局项, 则将其进入ConfigSetting.xml

履新机制的流程图

一、用户张开你的app。

二、插件初叶化,在后台进度运维进步加载器(update loader)。

三、Update
loader从config.xml取config-file配置1个url,并从此url加载1段json配置,然后它把那段json配置中的release版本号和当前的app已经设置的开始展览比较。假如区别进行下一步。

4、update loader使用app配置(application
config)中的content_url,去加载清单文件(manifest)。它会找寻自上次提高的话,哪些文件必要创新。

5、update loader从content_url下载更新文件。

陆、若是1切顺遂,发出三个“进级文件已经筹划好,能够设置了”的打招呼。

七、进级文件已安装,app重新进入创新过的页面。

音讯队列是遍布式系统中落到实处RPC的1种手腕。

金沙8331 1金沙8331 2View Code

    小编把涉及的五个类都位居了三个文书, 那样引入二个文书就能够

web内容是何等存款和储蓄和翻新的

每二个Cordova项目下都有3个www目录,这里存放全部的web内容。当cordova
build实行后,www的内容会拷贝到对应的platform的www目录下。于是那几个文件被打包到了app,app里的文本是只读的,不可更动,所以在app第3回开发银行的时候,将安置的web内容(www目录)复制到外部存款和储蓄。大家不想在拷贝进度中阻塞ui。我们如故会先加载app内置的index.html。然而下三次运维和换代,大家就从外表存款和储蓄加载index.html。

注:倘若app外壳须求追加新的Cordova插件或然原生功能,必须再一次上架外壳App到应用市肆。

一、新闻队列的中央使用流程

public partial  class WebConfig
    {
        /// <summary>
        /// 运营配置类
        /// </summary>
       public static void OnStart(string configFilePath, FileUpdate fileUpdate)
        {
            #region  落成配置文件独立
            //首回运转需求施行委托方法革新配置类
            fileUpdate(configFilePath);
            //运转文件监视
            Log4netFileWatchHelper.StartWatching(fileUpdate, configFilePath);
            #endregion
        }
        /// <summary>
        /// Config/Web 文件夹的磁盘路线
        /// </summary>
       public static string ConfigFilePathRoot
       {
           get;
           set;
       }       
    }

金沙8331 3金沙8331 4

app配置文件

①、chcp.json里面有个release设置,那一个指明了web内容的本子。它由命令行客户端自动生成,格式是年月日
每趟发表,插件在外表存储自动生成三个以这一个release版本为名字的目录,然后把web内容全方位平放这在那之中。release版本号成了url的1部分。

注:修改了一些文本,重新启航了app,不过看看的是旧的页面,原因是插件用的是旧版本的web内容(外部存储中)。若要清除缓存。

二、大家宣布新版之后,插件供给下载新的web文件,发生情形如下

安装更新

假设:

       一:包罗二个最首要方法OnStart,能够对配置文件实行起头化,其实这几个开端化也能够省略掉,因为一心能够将开始化配置类成为延迟加载情势。
      
二:二个布署文件路线的属性,它标志了此项目类具有配置文件的存放目录,能够在程序初阶化时钦赐此属性。

    using System;
    using System.Collections.Generic;
    using System.Xml.Linq;
    using System.Security.Cryptography;
    using System.IO;
    /// <summary>
    /// 设置项的帮助类
    /// </summary>
    public class ConfigSettingTool
    {
        /// <summary>
        /// 保存读取时, 是否加密解密; 设为false, 可以方便调试
        /// 其实也只能防小白, 随便反编译一下就啥都漏出来了
        /// </summary>
        private static bool isEncrypt = false;

        /// <summary>
        /// 默认的配置文件名
        /// </summary>
        public static readonly string DefaultXmlFileName = "ConfigSetting.xml";

        /// <summary>
        /// 获取XDocument, 解密失败、XML结构不合理, 都会根据模板重新生成一个
        /// </summary>
        /// <param name="xmlFileName"></param>
        /// <param name="msg"></param>
        /// <returns>确保返回如下格式
        /// <?xml version="1.0" encoding="utf-8" standalone="yes"?>
        /// <Setting>
        ///   <SingleSetting></SingleSetting>
        /// </Setting>
        /// </returns>
        public static XDocument GetXDocument(string xmlFileName, out string msg)
        {
            msg = null;
            if (!System.IO.File.Exists(xmlFileName))
            {
                msg = "配置文件不存在, 创建默认配置文件";
                return new XDocument(new XDeclaration("1.0", "utf-8", "yes"), new XElement("Setting", new XElement("SingleSetting")));
            }
            try
            {
                var textContent = System.IO.File.ReadAllText(xmlFileName);
                textContent = isEncrypt ? Decrypt(textContent) : textContent;
                var xdoc = XDocument.Parse(textContent);
                if (xdoc.Root.Name != "Setting")
                {
                    throw new Exception("根节点不是 Setting");
                }
                if (xdoc.Root.Element("SingleSetting") == null)
                {
                    throw new Exception("没有 SingleSetting 节点");
                }
                return xdoc;
            }
            catch
            {
                msg = "配置文件不是标准格式, 删除后, 创建默认配置文件";
                return new XDocument(new XDeclaration("1.0", "utf-8", "yes"), new XElement("Setting", new XElement("SingleSetting")));
            }
        }


        /// <summary>
        /// 将xml信息读出到settingArray, 如果缺少某项设定则增加到xdoc
        /// </summary>
        /// <param name="xdoc"></param>
        /// <param name="settingList">通常就是GlobalSetting.DefaultGlobalSettingArray</param>
        public static void ReadValueToSettingArray(XDocument xdoc, List<ConfigureItemModel> settingArray)
        {
            var singleSettingElement = xdoc.Root.Element("SingleSetting");
            foreach (var configureItem in settingArray)
            {
                configureItem.ErrorMsg = null;
                var element = singleSettingElement.Element(configureItem.Name);
                if (element == null)
                {
                    element = new XElement(configureItem.Name, configureItem.DefaultValue, new XAttribute("Caption", configureItem.Caption), new XAttribute("Description", configureItem.Description), new XAttribute("DefaultValue", configureItem.DefaultValue), new XAttribute("CanBeEmpty", configureItem.CanBeEmpty));
                    singleSettingElement.Add(element);
                }
                configureItem.Value = string.IsNullOrWhiteSpace(element.Value) ? "" : element.Value.Trim();
            }
        }


        /// <summary>
        /// 将xml信息读出到settingArray
        /// </summary>
        /// <param name="xdoc"></param>
        /// <param name="settingList">通常就是GlobalSetting.DefaultGlobalSettingArray</param>
        public static void ReadConfig(XDocument xdoc, out List<ConfigureItemModel> settingList)
        {
            settingList = new List<ConfigureItemModel>();

            var singleSettingElement = xdoc.Root.Element("SingleSetting");
            foreach (var element in singleSettingElement.Elements())
            {
                var captionAttribute = element.Attribute("Caption");
                var caption = captionAttribute != null ? captionAttribute.Value : "";

                var name = element.Name.ToString();
                var value = element.Value.ToString();

                var descriptionAttribute = element.Attribute("Description");
                var description = descriptionAttribute != null ? descriptionAttribute.Value : "";

                var defaultValueAttribute = element.Attribute("DefaultValue");
                var defaultValue = defaultValueAttribute != null ? defaultValueAttribute.Value : "";

                var canBeEmpty = false;
                try
                {
                    canBeEmpty = bool.Parse(element.Attribute("CanBeEmpty").Value);
                }
                catch { }

                var errorMsgAttribute = element.Attribute("ErrorMsg");
                var errorMsg = errorMsgAttribute != null ? errorMsgAttribute.Value : "";


                var configureItem = new ConfigureItemModel(caption, name, defaultValue, description, canBeEmpty) { Value = value, ErrorMsg = errorMsg };
                settingList.Add(configureItem);
            }
        }


        /// <summary>
        /// 尝试解析设置内容 到 目标class
        /// </summary>
        /// <param name="settingList">通常就是GlobalSetting.DefaultGlobalSettingArray, 配置项设置不合理时, 会将错误信息保存到ErrorMsg</param>
        /// <param name="targetSettingClass">通常就是GlobalSetting</param>
        /// <returns>成功, true; 失败: false</returns>
        public static bool TryParseConfig(List<ConfigureItemModel> settingArray, Type targetSettingClass)
        {
            bool isAllSuccess = true;
            foreach (var configureItem in settingArray)
            {
                configureItem.ErrorMsg = null;
                configureItem.Value = string.IsNullOrWhiteSpace(configureItem.Value) ? "" : configureItem.Value.Trim();
                if (configureItem.Value == "" && configureItem.CanBeEmpty == false)
                {
                    configureItem.ErrorMsg += "该项值不能为空, 请手动填写该值;";
                    isAllSuccess = false;
                    continue;
                }

                var property = targetSettingClass.GetProperty(configureItem.Name);
                //如果 targetSettingClass 没有对应的静态属性, 则跳过
                if (property == null)
                {
                    continue;
                }
                object value = null;
                try
                {
                    value = Convert.ChangeType(configureItem.Value, property.PropertyType);
                    property.SetValue(null, value, null);
                }
                catch
                {
                    configureItem.ErrorMsg += configureItem.Value + "不能转换为" + property.PropertyType.Name + ", 请重新填写该值;";
                    isAllSuccess = false;
                    continue;
                }
            }
            return isAllSuccess;
        }


        /// <summary>
        /// 写入
        /// </summary>
        /// <param name="xmlFileName"></param>
        /// <param name="settingList">通常就是GlobalSetting.DefaultGlobalSettingArray</param>
        /// <returns>成功, null</returns>
        public static bool TrySaveToXML(string xmlFileName, List<ConfigureItemModel> settingArray, out string msg)
        {
            msg = null;
            var xdoc = GetXDocument(xmlFileName, out  msg);//原文件读出错误, 忽略即可, 因为settingArray会自动填充
            var singleSettingElement = xdoc.Root.Element("SingleSetting");

            foreach (var configureItem in settingArray)
            {
                var element = singleSettingElement.Element(configureItem.Name);
                if (element == null)
                {
                    element = new XElement(configureItem.Name, configureItem.Value);
                    singleSettingElement.Add(element);
                }
                else
                {
                    element.Value = configureItem.Value ?? "";
                }
                element.RemoveAttributes();
                element.Add(new XAttribute("Caption", configureItem.Caption));
                element.Add(new XAttribute("Description", configureItem.Description));
                element.Add(new XAttribute("DefaultValue", configureItem.DefaultValue));
                element.Add(new XAttribute("CanBeEmpty", configureItem.CanBeEmpty));
                if (!string.IsNullOrWhiteSpace(configureItem.ErrorMsg))
                {
                    element.Add(new XAttribute("ErrorMsg", configureItem.ErrorMsg));
                }
            }


            var textContent = xdoc.ToString();
            textContent = isEncrypt ? Encrypt(textContent) : textContent;
            try
            {
                System.IO.File.WriteAllText(xmlFileName, textContent);
                return true;
            }
            catch (Exception ex)
            {
                msg= "保存失败:" + ex.Message;
                return false;
            }
        }

        #region 加密解密部分
        private static byte[] DESKey = new byte[] { 11, 69, 93, 102, 172, 41, 18, 12 };
        private static byte[] DESIV = new byte[] { 75, 77, 46, 197, 78, 157, 23, 36 };

        /// <summary>
        /// 加密
        /// </summary>
        private static string Encrypt(string source)
        {
            string reValue = "";
            DESCryptoServiceProvider objDes = new DESCryptoServiceProvider();
            MemoryStream objMemoryStream = new MemoryStream();
            CryptoStream objCrytoStream = new CryptoStream(objMemoryStream, objDes.CreateEncryptor(DESKey, DESIV), CryptoStreamMode.Write);
            StreamWriter objStreamWriter = new StreamWriter(objCrytoStream);
            objStreamWriter.Write(source);
            objStreamWriter.Flush();
            objCrytoStream.FlushFinalBlock();
            objMemoryStream.Flush();
            reValue = Convert.ToBase64String(objMemoryStream.GetBuffer(), 0, (int)objMemoryStream.Length);
            return reValue;
        }

        /// <summary>
        /// 解密
        /// </summary>
        private static string Decrypt(string source)
        {
            string reValue = "";
            DESCryptoServiceProvider objDES = new DESCryptoServiceProvider();
            byte[] Input = Convert.FromBase64String(source);
            MemoryStream objMemoryStream = new MemoryStream(Input);
            CryptoStream objCryptoStream = new CryptoStream(objMemoryStream, objDES.CreateDecryptor(DESKey, DESIV), CryptoStreamMode.Read);
            StreamReader objStreamReader = new StreamReader(objCryptoStream);
            reValue = objStreamReader.ReadToEnd();
            return reValue;
        }
        #endregion
    }

    /// <summary>
    /// 单个设置项
    /// </summary>
    /// <remarks>由于XML中不能保存null, 所以所有属性都不会被设置为null</remarks>
    public class ConfigureItemModel
    {
        /// <summary>
        /// 单个设置项
        /// </summary>
        /// <param name="captionParam">显示名称</param>
        /// <param name="nameParam">参数名称</param>
        /// <param name="defaultValueParam">默认值</param>
        /// <param name="descriptionParam">描述, 该项不设定时候, 显示默认值</param>
        /// <param name="canBeEmptyParam">能否为空字符串</param>
        public ConfigureItemModel(string captionParam, string nameParam, string defaultValueParam, string descriptionParam = "", bool canBeEmptyParam = false)
        {
            Caption = captionParam;
            Name = nameParam;
            Description = descriptionParam;
            DefaultValue = defaultValueParam;
            CanBeEmpty = canBeEmptyParam;
        }



        private string caption = "";
        /// <summary>
        /// 显示名称
        /// </summary>
        public string Caption
        {
            get { return caption; }
            set { caption = string.IsNullOrWhiteSpace(value) ? "" : value; }
        }



        private string name = "";
        /// <summary>
        /// 参数名称
        /// </summary>
        public string Name
        {
            get { return name; }
            set { name = string.IsNullOrWhiteSpace(value) ? "" : value; ; }
        }


        private string description = "";
        /// <summary>
        /// 说明, 如果该值没有赋值, 则显示DefaultValue
        /// </summary>
        public string Description
        {
            get { return string.IsNullOrWhiteSpace(description) ? defaultValue : description; }
            set { description = string.IsNullOrWhiteSpace(value) ? "" : value; }
        }


        private string defaultValue = "";
        /// <summary>
        /// 默认值
        /// </summary>
        public string DefaultValue
        {
            get { return defaultValue; }
            set { defaultValue = string.IsNullOrWhiteSpace(value) ? "" : value; }
        }


        /// <summary>
        /// 能否为空字符串
        /// </summary>
        public bool CanBeEmpty { get; set; }

        /// <summary>
        /// 能否为空字符串 的字符串形式
        /// </summary>
        public string CanBeEmptyString { get { return CanBeEmpty ? "是" : "否"; } }


        private string innerValue = "";
        /// <summary>
        /// 值
        /// </summary>
        public string Value
        {
            get { return innerValue; }
            set { innerValue = string.IsNullOrWhiteSpace(value) ? "" : value; ; }
        }


        private string errorMsg = "";
        /// <summary>
        /// 错误信息
        /// </summary>
        public string ErrorMsg
        {
            get { return errorMsg; }
            set { errorMsg = string.IsNullOrWhiteSpace(value) ? "" : value; ; }
        }

    }

Cordova Hot Code Push 命令行客户端

     第二:自定义的布局文件类,比如大家能够加多1个数码访问的配置类。

View Code

本地开垦扩大

本地配置流程

分析:

 

 

Cordova配置项

在根目录下的config.xml文件实行安顿

<chcp>
    <config-file url="https://e4c6b23c.ngrok.io/chcp.json" />
</chcp>

机关加载和装置

<chcp>
<auto-install enabled="false" />
<auto-download enabled="false" />
</chcp>

安顿文件
chcp.json和chcp.manifest

金沙8331 5金沙8331 6View Code

   产品里建一个 GlobalSetting 类, 里面的配置项都无法不是  static 属性,
然后进入 public static List<ConfigureItemModel>
DefaultGlobalSettingArray 保存暗许设置

Application config app配置

新型版本的release音信,放在www目录下,文件名叫chcp.json,这一个文件也被打包到外壳app内。
实践cordova-hcp命令,自动生成chcp.json和chcp.manifest文件

cordova-hcp init

实施此命令颁发新的release版本

cordova-hcp build

注:cordova的热更新,在怎么着意况下,需要更新app外壳。

在此间大家作为示范,,每当注册三个admin的以往,大家异步写一条日志log数据到数据库。

 [Serializable ]
    public  class DataAccessConfig
    {   
        #region 要求体系化的布署文件属性
        /// <summary>
        /// 数据库消息列表
        /// </summary>
        public List<DataBase> DataBaseList { get; set; }
        #endregion

金沙8331 7金沙8331 8

update

下边包车型大巴比喻也是对代码的表达。

        #region 相对于Config文件夹的子文件路线,无需体系化.
        /// <summary>
        /// 相对于Config文件夹的子文件路径,不要求种类化.
        /// </summary>
        [NonSerialized()]
        private static string m_SubFilePath = @”DataAccessConfig.config”;
        /// <summary>
        /// 子文件路线(排除config文件夹路线后的有个别)
        /// </summary>
        public static string SubFilePath
        {
            get { return m_SubFilePath; }
            set { m_SubFilePath = value; }
        }
        #endregion
        public static DataAccessConfig CreateInstance()
        {
            FileUpdate fileUpdate = new FileUpdate(WebConfig.DataAccessConfigOnUpdate);
            string configFilePath = WebConfig.ConfigFilePathRoot + DataAccessConfig.SubFilePath;
            if (!File.Exists(configFilePath))
            {
                return null;
            }
            DataAccessConfig config = SerializationHelper.Load(typeof(DataAccessConfig), configFilePath) as DataAccessConfig;
            //运行文件监视
            Log4netFileWatchHelper.StartWatching(fileUpdate, configFilePath);
            return config;
        }     
    }
   
    public partial class WebConfig
    {
        #region 第二步:为DataAccessConfig类添参加口
        /// <summary>
        /// 属性对应的个体变量
        /// </summary>
        private static DataAccessConfig m_DataAccessConfig = null;
        /// <summary>
        /// 属性访问器.通过WebConfig.SimpleFileDemoConfig能够访问此类.
        /// </summary>
        public static DataAccessConfig DataAccessConfig
        {
            get
            {
                if (m_DataAccessConfig == null)
                    Interlocked.CompareExchange<DataAccessConfig>(ref m_DataAccessConfig,
                        DataAccessConfig.CreateInstance(), null);
                return m_DataAccessConfig;
            }
        }
        #endregion

    /// <summary>
    /// 全局设定
    /// </summary>
    /// <remarks>所有属性都必须是 static , 即 类属性</remarks>
    public class GlobalSetting
    {
        public static List<ConfigureItemModel> DefaultGlobalSettingArray = new List<ConfigureItemModel>()
        {
            new ConfigureItemModel("数据库的主机地址","ConnectionStringHost", "127.0.0.1"),
        };

        /// <summary>
        /// 数据库的主机地址
        /// </summary>
        public static string ConnectionStringHost { get; set; }
    }
android_identifier(鉴定人)

apk报名,要是钦点了,指点用户到谷歌 Play Store的app页面

        #region 第1步:为DataAccessConfig类加多履新函数
        /// <summary>
        /// 更新函数
        /// </summary>
        /// <param name=”status”></param>
        public static void DataAccessConfigOnUpdate(object status)
        {
            lock (WebConfig.DataAccessConfig)
            {
                try
                {
                    m_DataAccessConfig = DataAccessConfig.CreateInstance();
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }
        #endregion
    }

View Code

Content manifest内容清单

剧情清单描述了web项目具备文件的状态
基于那个清单,插件才知道怎么着文件被移除了,什么文件更新或新扩充了。

生成chcp.manifest文件的通令

cordova-hcp build

当server1施行二个”

 

 

Build options build设置

假诺想在使用build命令的时候改造插件的职位,那么供给利用chcpbuild.options文件。文件必须放在Cordova项目根目录

{
  "dev": {
    "config-file": "https://dev.company_server.com/mobile/www/chcp.json"
  },
  "production": {
    "config-file": "https://company_server.com/mobile/www/chcp.json"
  },
  "QA": {
    "config-file": "https://test.company_server.com/mobile/www/chcp.json"
  }
}

build app的时候,转为开荒要选用的服务器,可实践

cordova build -- chcp-dev

实施此命令后,cofig.xml文件中的chcp配置就能够猎取更改

当要求上架app的时候,大家build的吩咐

cordova build --release

server贰循环接收server三中的音讯队列中的新闻,并将那个log消息写入数据库。

      
那段代码正是大家要完结的局地,不能够复用,总觉的急需改正一下,难题分析:
      
壹:由于要求利用静态调用格局,为此要想透过WebConfig的静态属性调用,就要求在WebConfig类中增添自定义配置类的性质,比如我们扩充贰个Hotel相关的布署类,而要想WebConfig.Hotel,就须求追加二个静态属性情局达成。这里可以稍微修改下,即最终在布局自定义配置类时只编写自定义配置类,而不用去编写WebConfig类。
       二:自定义配置类中的CreateInstance,也要思考复用。

  具体的以身作则请参考 ConfigSettingToolTest, 第贰遍运营时会报错: 进级地址
不可能为空, 使用  配置文件编写工具二.exe 为其赋值后, 就足以健康运维了

热更新流程

1、app启动

2、从服务器请求chcp.json文件,覆盖本地的chcp.json文件

三、服务器重返的chcp.json文件与app里的chcp.json文件做相比较,剖断release版本

四、要是服务器chcp.json文件的release时间超过app里的chcp.json的release时间,就立异财富

伍、发送请求,更新服务器的chcp.manifest文件

陆、服务器再次回到chcp.manifest文件与app里的chcp.manifest文件内容比较

7、假诺有不均等的hash值

八、对服务器请求新的财富

九、请求成功后财富覆盖本地能源

 

      
原则便是自定义的安插类不珍爱怎么着读取配置文件,只关怀本身的计划属性就能够。
       
       改正后的本子:       
一:针对WebConfig.自定义类情势。这里笔者修改的办法也不是最棒的,用起来未有改变前的胜利,但代码确实精简了。思路就是在WebConfig类中生成2个泛型配置类。
       

 

二、音讯队列的职能

public partial class WebConfig<T> :WebConfig where T:class

ConfigSettingToolTest 
           
ConfigEditer

 

 

 

      
这里新扩展了WebConfig的泛型版本,而将WebConfig提取成基类,WebConfig类中蕴藏一本性质,即布置文件所在文件夹路线。
      

三、音信队列的三种方式

 /// <summary>
        /// Config/Web 文件夹的磁盘路线
        /// </summary>
        public static string ConfigFilePathRoot
        {
            get;
            set;
        }

 

 

       
WebConfig<T>主要目标是为着生成T类型的布署类,这里将原本在自定义配置类中的代码提取到WebConfig<T>中:

4、实例(基于P2P实现)

金沙8331 9金沙8331 10View Code

肆.壹、全部代码结构:

         /// <summary>
        /// 属性对应的私家变量
        /// </summary>
        private static T m_DataAccessConfig ;
        /// <summary>
        /// 属性访问器.通过WebConfig.SimpleFile德姆oConfig能够访问此类.
        /// </summary>
        public static T DataAccessConfig
        {
            get
            {
                if (m_DataAccessConfig == null)
                    Interlocked.CompareExchange<T>(ref m_DataAccessConfig,
                        CreateInstance(), null );
                return m_DataAccessConfig;
            }
        }        
         /// <summary>
        /// 更新函数
        /// </summary>
        /// <param name=”status”></param>
        public static void DataAccessConfigOnUpdate(object status)
        {
            lock (WebConfig<T>.DataAccessConfig)
            {
                try
                {
                    m_DataAccessConfig = CreateInstance();
                }
                catch (Exception ex)
                {
                    throw ex;
                }
            }
        }

金沙8331 11

    
        二:将自定义配置类生成实例的章程也开始展览包装。
         

 

private static T CreateInstance()
        {
            SubFilePath = typeof(T).Name+”.config”;
            string configFilePath = WebConfig<T>.ConfigFilePathRoot + SubFilePath;
            if (!File.Exists(configFilePath))
            {
                return null;
            }
            T config = SerializationHelper.Load(typeof(T), configFilePath) as T;
            //运转文件监视
            FileWatch(configFilePath);
            return config;            

肆.二、模块注重关系

        }

金沙8331 12

        
       
有了下面的领取封装,上面就是改版后的自定义类了,到近日截至,大家编辑3个自定义配置类的本钱已经比相当低了:
        

注:箭头的对准正是当下模块所依赖的模块。(eg.rpcWeb依赖data)

[Serializable]
    public class MyConfig : WebConfig<DataAccessConfig>
    {
        #region 需求系列化的布置文件属性
        public string ExpenseTemplateFile { get; set; }
        public bool Switch { get; set; }
        #endregion      
    }

 

 

       最后我们抬高3个配备文件,名称和自定义配置类保持1致。
      

4.3、代码

  <?xml version=”1.0″ encoding=”utf-8″ ?>
<MyConfig>
  <ExpenseTemplateFile>ssssss3</ExpenseTemplateFile>
  <Switch>false</Switch>
</MyConfig>

代码全部没变,只列出一部分新添代码,完整代码从文首的github进行clone就可以。

    
      客户端调用格局:   

4.3.1、ssmm0

WebConfig<MyConfig>.DataAccessConfig.Switch

pom.xml

      

金沙8331 13金沙8331 14

      
尽管此番改版并不周全,但在八个品类中选拔时依然起到了极大的效应,编写轻便,调用方便,唯壹不顺心的正是索要这么写:WebConfig<MyConfig>,未有在此以前的WebConfig.MyConfig格局来的舒适。

    <!-- 管理子模块 -->
    <modules>
        <module>common</module><!-- 通用类模块 -->
        <module>cache</module><!-- 缓存模块 -->
        <module>rpc</module><!-- rpc模块 -->
        <module>data</module><!-- 封装数据操作 -->
        <module>userManagement</module><!-- 具体业务1-人员管理系统,这里的userManagement部署在serverA上(配合rpcWeb测试rpc) -->
        <module>rpcWeb</module><!-- 具体业务2-用于测试RPC的另一台机器,这里的rpcWeb项目部署在serverB上 -->
    </modules>

            <!-- 日志:若没有,activemq获取连接报错 -->
            <dependency>
                <groupId>org.slf4j</groupId>
                <artifactId>slf4j-log4j12</artifactId>
                <version>1.5.11</version>
            </dependency>

View Code

表明:只列出有些新添的代码。

注意:

相关文章

发表评论

电子邮件地址不会被公开。 必填项已用*标注

网站地图xml地图