随心所欲

做个幸福的人
posts - 152, comments - 1472, trackbacks - 28, articles - 0
  博客园 :: 首页 :: 新随笔 ::  :: 订阅 订阅 :: 管理

实现目标

1:所加载的dll分布在不同的文件夹下,可以不再运行目录bin下。以创建AppDomain的方式加载/卸载

2:运行中可以自动监测dll的版本,如果dll又更新,则自动卸载原来的dll,重新加载新的程序集(当然也就得必须可以替换正在运行中的dll)

3:加载程序集中的类可以访问主程序域的方法(主程序域中的类当然可以访问自程序域中的实例的方法)


优点:


1:可以在运行中替换具体实现,不需要停止程序再加载

2:在不同程序域里的实现互相隔离,代码运行比较安全,一个实现出了问题,可以只卸载那一个AppDomain就可以



几个问题:

1:子程序域中无法访问主程序域中的一些配置文件,比如app.config

2:运行速度会慢一些,因为需要反复的 序列化/反序列化 。

3:调试复杂,不同程序域的调试就像递归函数一样,难以调试

4:不再是传实例引用,所有的都是传值的方式。


实现思路

1:通过创建不同的AppDomain来加载dll,通过卸载AppDomain来卸载dll

2:通过一个Proxy类(继承自MarshalByRefObject)来访问具体的实现,一定不能返回具体实现的实例,而是要通过传递参数,在代理中执行,然后返回结果

3:子程序域中实现通过一个ProxyBack(MarshalByRefObject)的代理来访问主程序域中的方法,这个ProxyBack使用TCP信道来通讯 (Microsoft .Net Remoting)

4:所有需要在不同域之间传递的参数/返回值 都必须是可以序列化的。

5:制作一个DLLWatcher来监视dll所在文件夹的状态



具体实现

1:代理类 创建不同的程序域 访问子程序域中的方法

      http://dlwang2002.cnblogs.com/archive/2005/10/18/257425.html

2:使用Microsoft .Net Remoting技术,制作ProxyBack

    关于Microsoft .Net Remoting技术,Wayfarer's Prattle有很详细的解释http://www.cnblogs.com/wayfarer/archive/2004/07/30/28723.html

    这里主要看这几个主要的实现和注意事项


   这是代理的Server端,在乎程序域中启动,侦听
   public ProxyBackServer()
  {
   if(!isStart)
    {
      TcpChannel chan = new TcpChannel(8085);
       ChannelServices.RegisterChannel(chan);
 
      RemotingConfiguration.RegisterWellKnownServiceType(typeof( xxx.ProxyBackServer),
     "CallLocalSM",WellKnownObjectMode.SingleCall);
      isStart=true;
    }
   方法:注意所传递的都必须是可以序列化的对象,在需要加载dll的文件夹下也要有这个传递对象实现的程序集。
    public IStateDictionary RunService(IStateDictionary request)

   在这个Server的析构函数中要释放这个TCP信道

  ~ProxyBackServer()
//  {
//   IChannel[] channels = ChannelServices.RegisteredChannels;
//   //关闭指定名为CallLocalSM的通道;
//   foreach (IChannel eachChannel in channels)
//   {
//    if (eachChannel.ChannelName == "CallLocalSM")
//    {
//     TcpChannel tcpChannel = (TcpChannel)eachChannel;
//
//     //关闭监听;
//     tcpChannel.StopListening(null);
//
//     //注销通道;
//     ChannelServices.UnregisterChannel(tcpChannel);
//    }
//   }
//
//  }



     这是代理的Client端,子程序域访问主程序域的时候使用
   public ProxyBackClient()
  {
   if(!isReg)// boot once
   {
      TcpChannel chan = new TcpChannel();
      ChannelServices.RegisterChannel(chan);
       isReg=true;
   }
   _server = (ProxyBackServer)Activator.GetObject(
    typeof(xxxx.ProxyBackServer), "tcp://localhost:8085/CallLocalSM");
     if (_server == null) 
    {
      throw new Exception("can not connect to local host to get instance of sm");
     }

}
   public IContext RunService(IContext ctx)
  {
   try
    {
       ctx.Response= this._server.RunService(ctx.Request);
      return ctx;
..........

    }

}
   

 3:DllWatcher

   ............

   _mask = "*.dll";
   // initialize watcher to watch the process directory
   this.dllWatcher = new FileSystemWatcher();
   this.dllWatcher .Path = _path;
   this.dllWatcher .Filter = _mask;
   this.dllWatcher .Changed +=  new FileSystemEventHandler(File_OnChanged);
   this.dllWatcher .Created +=  new FileSystemEventHandler(File_OnChanged);
   this.dllWatcher .Deleted +=  new FileSystemEventHandler(File_OnChanged);
   // tell it to start watching
   this.dllWatcher .EnableRaisingEvents = true;

在接收到事件之后,调用主域中的方法卸载AppDomain,然后重新加载 就可以
 

Feedback

#1楼   回复  引用  查看    

2005-10-31 23:40 by 补丁      
嗯...动态加载/卸载倒是解决了,还是觉得搞得好像有点复杂
而且比不能卸载得加载慢不少....鱼与熊掌啊

#2楼   回复  引用  查看    

2005-11-01 08:47 by Cavingdeep      
根据不同的情况采用你解决方案的一部分我觉得就够了。^_^

#3楼   回复  引用  查看    

2005-11-01 08:48 by 风满袖      
.net 2.0大大提高了AppDomain之间通信的效率。

#4楼[楼主]   回复  引用  查看    

2005-11-01 08:55 by 随心所欲      
其实那四个问题哪一个都比较麻烦

to 风满袖:
我在1.1下测试,tcp通讯序列化的代价和soap序列化代价的比例是1:9
在序列化之后的结果大于有3M左右的时候,TCP用了200ms,soap用了1800ms。
如果.net2.0可以降低到100ms以下的话,那么速度就不是太大的问题了。

#5楼   回复  引用  查看    

2006-02-17 17:04 by U2U      
不需要这样,可以引入[Assembly]空间

可以直接载入DLL程序集

作者的解决方案已经应该归到.net Remoting的初级技术的领域了

#6楼   回复  引用  查看    

2006-02-17 17:09 by U2U      
提出几个有趣的方法:
1、充分利用GAC,先创建一个强文件名,然后就可以很方便地进行版本控制

2、让用户运行的是一个"Appliction Loader",并且结合Webservice,获取服务器版本并对当前版本进行对比,询问用户是否下载,然后直接用[Assembly].loadfrom 服务器的文件路径,例如http://www.U2U.org/Application_Build0359.dll">http://www.U2U.org/Application_Build0359.dll,([Assembly]可以从网上下载程序集,但是跟创建远程对象的原理不一样,它是把文件下载到GAC然后运行。)

这个dll可以创建一个Module1.Main()方法,然后其它载入窗体之类的就在这个方法里面定义,然后——搞定!



发表评论

昵称: [登录] [注册]

主页:

邮箱:(仅博主可见)

评论内容:

  登录  注册

[使用Ctrl+Enter键快速提交评论]

0 265810




相关文章:

相关链接:

Google