某些软件系统作为服务来运行,需要长时间的高可靠的运行,也许一年时间内只允许停顿一次维护,那么如果在运行过程中我们需要更新某一功能模块,该怎么办呢?本文描述了怎样用Net的AppDomain机制来解决这一问题。
<?xml:namespace prefix = o ns = "urn:schemas-microsoft-com:office:office" />
不许重新编译整个系统就可以更新某个模块的方法我们都知道,那就是用反射功能,比如Assembly.Load,可以在代码层动态加载某个模块。但要实现真正的运行过程中动态更换,我们必须有一个机制,首先把旧的版本从进程中卸载,然后载入新的版本,不幸的是,我们没有一个Assembly.UnLoad方法,我们必须寻找其他途径。
AppDomain为解决此问题提供了更好的方案,由于一个进程内可以有多个AppDomain,每个AppDomain之间隔离性很好,我们可以在运行过程中加载和卸载某个AppDomain。
AppDomain怎么通信的呢?我们想到了Remoting,Remoting的一个重要功能是可以支持AppDomain之间的通信,我们需要一个实现MarshalByRefObject接口的类来作为调用的通道。
下面是代码实现,两个Assembly,AppDomainTest,作为我们的主系统,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)。系统运行的任何时候你将可以更新模块文件。
分享到:
相关推荐
linux下netlink机制实现usb热插拔事件并获取诸如厂商编号、协议号等获取相关信息,在不采用udev机制的情况下,可以参考的一种实现方式
使用AppDomain实现不重启进程的dll的重复加载
实现android usb光驱 光盘支持 光驱热插拔 光盘热插拔 1.增加vold对Iso9660光盘自动挂载的支持,基于RK3188_RK3066_R-BOX_ANDROID4.4.2-SDK_V1.0.0_140318,也可用于mid 2.实现Iso9660.cpp为vold实现的domount接口 3...
带freertos操作系统的lwip移植模板,实现网线的热插拔,芯片为STM32F746,其中还包含自己的学习总结,移植改进和野火的官方教程。
hotplug 热插拔 测试程序,程序源码,运行程序,插入和拔出U盘,程序会捕获热插拔信息。
西门子PLC系统中热插拔功能的应用与说明pdf,西门子PLC系统中热插拔功能的应用与说明
正点原子阿波罗F429+STM32CubeMX+LAN8720+LwIP:不带操作系统实现网络热插拔.使用LWIP的最基本的功能,就是能PING通,能热插拔网线.没其它的功能.
通过注册表实现串口热插拔,方法简单,嘿嘿,利用VS2005编译通过
正点原子阿波罗F429+STM32CubeMX+LAN8720+LWIP+Freetos:带操作系统实现网络热插拔。
SIEMENS PLC的热插拔选型与应用pdf,西门子PLC资料大全-西门子内部培训资料,SIEMENS PLC 系统中热插拔功能的应用与说明 SIEMENS的PLC 控制系统中: ü S7-200 系列PLC不支持热插拔功能; ü S7-300 CPU直接带I/O模块...
西门子PLC热插拔功能实现及注意事项、组态及配置。
本文分析了CPCI热插拔的原理及实现方法,重点介绍了热插拔系统的状态转移过程、硬件结构及设计思路,最后给出通过专用的热插拔控制器LT1643及桥芯片PLX9656实现热插拔的详细设计方法,对于有意开发热插拔板卡的设计...
关于嵌入式Qt开发中不支持热插拔鼠标的解决方案,需更改Qt源码重新编译
通过注册表实现检测串口热插拔,方法简单实用,嘿嘿,利用VS2005编译通过
Linux 2.6.10内核下PCI Express Native热插拔框架的实现机制 Linux 2.6.10内核下PCI Express Native热插拔框架的实现机制
热插拔保护电路设计及实例。
QT5.9.8鼠标键盘热插拔默认情况是使用udev进行热插拔的,但很多情况下嵌入式系统udev配置不完整, 可能需要systemd里面的udev组件才能正常工作 鉴于此,参考网上的朋友的修改例子,不使用udev也能正常检测键鼠热插...
AIX系统更换热插拔设备 AIX系统更换热插拔设备
课程设计大作业C++基于Qt实现系统中设备管理功能(热插拔监测)源码。 主要功能 通过继承QAbstractNativeEventFilter接口实现串口热插拔监测功能; 通过在QWidget中重写nativeEvent实现串口热插拔监测功能; 通过一...
思科交换机热插拔手册:描述了思科交换机(高端)在热插拔时注意的事项,及问题CASE的解决。 全中文说明。通俗易懂。