`
lovnet
  • 浏览: 6710723 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

用应用程序域实现软件系统的热插拔

阅读更多

某些软件系统作为服务来运行,需要长时间的高可靠的运行,也许一年时间内只允许停顿一次维护,那么如果在运行过程中我们需要更新某一功能模块,该怎么办呢?本文描述了怎样用NetAppDomain机制来解决这一问题。

<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />

不许重新编译整个系统就可以更新某个模块的方法我们都知道,那就是用反射功能,比如Assembly.Load,可以在代码层动态加载某个模块。但要实现真正的运行过程中动态更换,我们必须有一个机制,首先把旧的版本从进程中卸载,然后载入新的版本,不幸的是,我们没有一个Assembly.UnLoad方法,我们必须寻找其他途径。

AppDomain为解决此问题提供了更好的方案,由于一个进程内可以有多个AppDomain,每个AppDomain之间隔离性很好,我们可以在运行过程中加载和卸载某个AppDomain

AppDomain怎么通信的呢?我们想到了Remoting,Remoting的一个重要功能是可以支持AppDomain之间的通信,我们需要一个实现MarshalByRefObject接口的类来作为调用的通道。

下面是代码实现,两个AssemblyAppDomainTest,作为我们的主系统,NewDomainFunction,作为我们需要频繁升级的模块。主程序每隔50秒(现实中太短了,这里只是演示)把系统的日志文件压缩,只压缩最老的100文件。NewDomainFunction实现了文件压缩的功能。

首先看主程序:

static void Main()

{

Console.WriteLine("Start test...");

while(true)

{

Console.WriteLine("Start zip log files...");

Evidence adevidence = AppDomain.CurrentDomain.Evidence;

AppDomain newDomain =System.AppDomain.CreateDomain("MyDomain",adevidence);

RemoteHelper helper =

(RemoteHelper)newDomain.CreateInstanceAndUnwrap("AppDomainTest","AppDomainTest.RemoteHelper");

helper.ZipLogFile(100);

AppDomain.Unload(newDomain);

Console.WriteLine("Zip log files done...");

System.Threading.Thread.Sleep(50000);

}

}

这里是接口类RemoteHelper:

public class RemoteHelper:MarshalByRefObject

{

public RemoteHelper()

{

}

public void ZipLogFile(int fileNumber)

{

string funFullName=System.Configuration.ConfigurationSettings.AppSettings["NewDomainFunction"];

if(funFullName.Length==0)

return;

Assembly asm=Assembly.Load(funFullName);

Type zipLogType=asm.GetType("NewDomainFunction.ZipLog");

Object[] zipLogArguments=new object[]{fileNumber};

MethodInfo zipLogMethodInfo =

zipLogType.GetMethod("ZipFiles");

zipLogMethodInfo.Invoke(Activator.CreateInstance(zipLogType),

zipLogArguments);

}

}

压缩模块的AssemblyName配置在config文件中

<add key="NewDomainFunction" value="NewDomainFunction, version=1.0.3273.17760, Culture=neutral, PublicKeyToken=cc277552af2314ec" />

当需要更新模块时,我们做两件事情:

1. 用新的Assembly替换掉旧的,直接覆盖,(当正在压缩文件时不可以,避开这个时间段)

2. 更改配置文件中的NewDomainFunction名字。

下面是压缩模块的代码:

public class ZipLog

{

public ZipLog()

{

}

public void ZipFiles(int fileNumber)

{

string fileName="Zip.log";

using(StreamWriter tw=new StreamWriter(new FileStream(fileName,FileMode.Append,FileAccess.Write)))

{

tw.WriteLine("Dll FullName:"+Assembly.GetExecutingAssembly().FullName);

tw.WriteLine("Zip time:{0}",DateTime.Now.ToString("yyyy.MM.dd hh:mm:ss"));

tw.Close();

//zip function...

}

}

}

启动主程序运行,然后把压缩模块的版本更新,替换旧的版本。下面是测试结果:

Dll FullName:NewDomainFunction, Version=1.0.3273.17760, Culture=neutral, PublicKeyToken=cc277552af2314ec

Zip time:2008.12.17 10:04:16

Dll FullName:NewDomainFunction, Version=1.1.3273.18113, Culture=neutral, PublicKeyToken=cc277552af2314ec

Zip time:2008.12.17 10:05:07

到此一切顺利,不过有个小地方我们感到不方便,那就是我们必须避开压缩文件的时候,如果正在压缩文件你去升级,比如我们在ZipFiles方法中加上一行:

System.Threading.Thread.Sleep(60000);

再运行,并且当你确定正在运行ZipFiles时删掉NewDomainFunction.dll文件,将出现下面的错误:

<?xml:namespace prefix = v ns = "urn:schemas-microsoft-com:vml" /><shapetype id="_x0000_t75" stroked="f" filled="f" path="m@4@5l@4@11@9@11@9@5xe" o:preferrelative="t" o:spt="75" coordsize="21600,21600"><stroke joinstyle="miter"></stroke><formulas><f eqn="if lineDrawn pixelLineWidth 0"></f><f eqn="sum @0 1 0"></f><f eqn="sum 0 0 @1"></f><f eqn="prod @2 1 2"></f><f eqn="prod @3 21600 pixelWidth"></f><f eqn="prod @3 21600 pixelHeight"></f><f eqn="sum @0 0 1"></f><f eqn="prod @6 1 2"></f><f eqn="prod @7 21600 pixelWidth"></f><f eqn="sum @8 21600 0"></f><f eqn="prod @7 21600 pixelHeight"></f><f eqn="sum @10 21600 0"></f></formulas><path o:connecttype="rect" gradientshapeok="t" o:extrusionok="f"></path><lock aspectratio="t" v:ext="edit"></lock></shapetype><shape id="_x0000_i1025" style="WIDTH: 359.25pt; HEIGHT: 89.25pt" type="#_x0000_t75"><imagedata o:title="" src="file:///C:%5CDOCUME~1%5Cluso%5CLOCALS~1%5CTemp%5Cmsohtml1%5C01%5Cclip_image001.png"></imagedata></shape>

这个错误的原因是系统会对进程里用到的原代码文件进行锁定。虽然在我们的例子中遇到这个时候的几率并不高,我们还是需要解决这个问题。解决方案是”Shadow copy”。在主程序CreateDomain之前建立AppDomainSetup,如下:

AppDomainSetup domaininfo = new AppDomainSetup();

domaininfo.ShadowCopyFiles="true";

domaininfo.ShadowCopyDirectories="file:///" + System.Environment.CurrentDirectory;

AppDomain newDomain =System.AppDomain.CreateDomain("MyDomain",adevidence,domaininfo);

这样的话,系统将把代码文件复制一份拷贝到其他目录(%userprofile%\local settings\application data\assembly)。系统运行的任何时候你将可以更新模块文件。

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics