服务器系统自动升级

    (本文名字取为“服务器系统自动升级”,实际上适用于所有应用程序自动升级的情况。)
    前文介绍了在服务器或客户端应用程序运行的过程中,插件如何自动升级、更新。基于前文相同的理由,AS、FS、IRAS也需要有自动升级的功能。
     与插件在运行时动态更新不同,服务器系统无法在运行时动态更新,只有在服务器系统重新启动的时候,才是自动升级的切入点。
(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   string  FileName ;
        
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接口定义修改一下即可适应。发挥你的创造力吧,欢迎和我交流你的想法!
    感谢关注!

上一篇文章:ESFramework介绍之(20)―― 插件自动升级

你可能感兴趣的:(服务器)