天天看點

ESFramework介紹之(22)―― 伺服器系統自動更新

    (本文名字取為“伺服器系統自動更新”,實際上适用于所有應用程式自動更新的情況。)

     與插件在運作時動态更新不同,伺服器系統無法在運作時動态更新,隻有在伺服器系統重新啟動的時候,才是自動更新的切入點。

(1)對于功能伺服器FS,可以采用持續/逐個更新的方式,即依次重新開機每個功能伺服器。這樣可以避免功能服務被中斷的情況發生。需要注意的是,隻有當目标FS上沒有功能請求時,才可重新開機該FS,否則,會導緻終端出現送出請求沒有響應的糟糕的使用者體驗。解決方案是:在FS重新開機之前,FS向對應的AS報告自己馬上将重新開機,這樣,AS不會再将請求分派到該FS上,一段時間後,本FS上就沒有功能請求了,于是可以順利重新開機了。

(2)對于IRAS與AS,則需要在重新開機前滿足本系統中沒有功能請求的條件,這可以通過一段時間不接受終端連接配接請求來做到。如果不能停止AS服務,則可以在更新本區域AS的時候,先啟用備用AS(可以通過端口映射在主AS和備用AS之間切換),當更新完成後,再切換回來。

    上面已經提到,當伺服器重新開機的時候是系統更新的切入點,這個切入點一般在Main函數中,并且位于Application.Run之前。如下面示例代碼所示:

                #region 系統更新                

                 if(MainClass.AsConfig.UpdateEnabled)

                {

                    DataCenterBase.Common.DataCenterHelper dataCenterHelper = (DataCenterBase.Common.DataCenterHelper)MainClass.SpringContext.GetObject("dataCenterHelper") ;

                    //如果有新版本

                    if(dataCenterHelper.GetAppServerNewVersion() > MainClass.Version)

                    {

                        string dir = System.IO.Path.GetDirectoryName(System.Windows.Forms.Application.ExecutablePath) ;

                        //更新程式的路徑

                        string updateExePath = dir + "\\" + MainClass.AsConfig.UpdateExeFileName ;

                        //啟動更新程式、并退出Main

                        EnterpriseServerBase.Common.AdvancedFunction.StartApplication(updateExePath) ;                

                        return ;

                    }

                }                

                #endregion

    需要自動更新的系統(比如上面的AS)中與更新相關的代碼大緻就這麼多,而本文我們重點要關注的是更新程式的實作。更新程式中核心接口是IAutoUpdator,位于ESFramework.Deploy命名空間中:

    public interface IAutoUpdator

    {

        void Start() ;

        void Cancel() ;

        IUpdateAssistant UpdateAssistant{set ;}

        IUiReporter      UiReporter{set ;} 

        event CbSimple UpdateFinished ;

    }

    Start方法将啟動更新過程,當更新完成的時候,将觸發UpdateFinished事件。更新開始時,IAutoUpdator讀取本地配置檔案中中應用程式和各個dll及相關檔案的版本資訊,然後與擷取的最新版本資訊相比較,以決定要更新哪些exe或dll,要删除哪些dll,要新下載下傳哪些exe或dll。針對每個exe或dll(甚至是有更新需要的重要檔案),都有一個OneUpdate對象與之對應。

    public class OneUpdate

        public UpdatingType UpdateType =  UpdatingType.Keep ;

        public string URL ;

        public string FilePath ;

        public string FileName ;

        public float  NewVersion ;

    /// <summary>

    /// UpdatingType 更新類型

    /// </summary>

    public enum UpdatingType

        Add ,Remove ,Update ,Keep //keep 表示不需更新

    這與前文的插件更新是相同的。有人會問,為什麼插件更新和應用程式更新不做成一樣的元件,這樣可以更多的複用啊?那是因為它們更新的模式是不一樣的,插件是動态更新的,而應用程式本身隻能是靜态更新的(當然,即使你動态更新了這個應用程式中加載的插件)。

    如果要順利的完成更新,有些必要的資訊需要擷取,比如最新版本的版本号,最新版本的伺服器系統exe及相關dll的下載下傳位址等等,這些是通過IUpdateAssistant接口提供的:

    public interface IUpdateAssistant

        IUpdateInformation[] GetUpdateInformation() ; //擷取最新版本檔案(exe、dll等)的下載下傳位址資訊

        FileInfo[]           GetFileInfoToUpdate() ;  //擷取要更新檔案(exe、dll等)的目前版本

        string               GetFileDirectory() ;

        // 通常操作本地配置檔案

        void                 ReviseFileVersion(string fileName ,float newVer) ; //增加或修改

        void                 RemoveFileVersion(string fileName) ;

    public class FileInfo

        public float  Version ;        

    public interface IUpdateInformation

        string FileName {get ;set ;}

        string URL {get ;set ;}

        float  Version {get ;set ;}

        string FileType {get ;set ;}         

        bool   IsValid{get ;set ;}

    不同的應用對IUpdateAssistant的實作是不同的。比如有的把最新版本的伺服器系統exe及相關dll存放在資料庫中,有的則可能放在某個Web上,以通過URL下載下傳擷取,等等。 有時新版本的應用程式可能需要删除舊版本中的某些不再需要的dll或其它輔助檔案,RemoveFileVersion用于這個目的。而當應用程式及相關dll更新完成後,就需要更改本地配置中的相應版本号,ReviseFileVersion提供了此功能。    

    IAutoUpdator通過IUiReporter接口将更新的進度資訊通知給UI,這樣使用者就對更新的程序就了然于胸了。

    public interface IUiReporter

        void Initialize(int minVal ,int MaxVal ,int val) ;

        void Set(int val) ;

        void ShowMessage(string msg) ;

    當有重要事件發生(比如網絡斷開)而導緻更新失敗,IUpdateAssistant會通過IUiReporter的ShowMessage方法給出通知。借助于上述的各個輔助接口,實作IAutoUpdator就非常easy了,實作源碼參見SFramework.Deploy.AutoUpdator類。

    在AutoUpdator的幫助下實作你自己的自動更新程式可以這樣做,首先建立一個新的WinForm項目,根據你應用的需要,實作上面列出的各個輔助接口,然後将這些實作裝配給AutoUpdator,就像這樣:

        public Form1()

        {            

            InitializeComponent();

            this.autoUpdator = new AutoUpdator() ;

            this.autoUpdator.UiReporter = this ;

            this.autoUpdator.UpdateAssistant = new UpdateAssistant() ;

            this.autoUpdator.UpdateFinished += new CbUpdate(autoUpdator_UpdateFinished);

        }

    然後在Load事件中啟動更新:

        private void Form1_Load(object sender, System.EventArgs e)

        {

            this.autoUpdator.Start() ;

    最後,當更新完成時,需要重新啟動新版本的應用程式:

        private void autoUpdator_UpdateFinished()

            MessageBox.Show("更新完成!") ;

            string apppath = System.IO.Directory.GetParent(Application.ExecutablePath).ToString();

            EnterpriseServerBase.Configure.XmlParser xmlParser = new EnterpriseServerBase.Configure.XmlParser(apppath + "\\" + "VersionInfo.xml");    

            //啟動新版本的應用程式

            Process downprocess = new Process();            

            downprocess.StartInfo.FileName = string.Format("{0}\\{1}" , apppath ,xmlParser.GetConfigValue("HostInfo" ,"StartAppName")) ;

            downprocess.Start();

            this.Close() ;

    目前的設計,主要支援從Url下載下傳最新版本的檔案(這可從IUpdateInformation接口的URL屬性看出),這也是最常見的方式。當然你可以将最新版本的檔案存放在資料庫中,這時就需要将IUpdateInformation接口定義修改一下即可适應。發揮你的創造力吧,歡迎和我交流你的想法!

    感謝關注!