zoukankan      html  css  js  c++  java
  • 完整的动态加载卸载程序集的解决方案

    实现目标

    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,然后重新加载 就可以

  • 相关阅读:
    PHP 使用命名空间(namespace),实现自动加载
    快捷方式不能使用的解决方法
    Python学习案例
    Linux下Tomcat的安装和部署
    关于Linux下的环境变量
    关于Linux下安装Oracle
    Linux下安装MySQLdb模块(Python)
    交换机VLAN的定义、意义以及划分方式
    让java程序在后台一直执行(例如putty关闭后后台程序继续运行)
    基于FTP服务器搭建yum源
  • 原文地址:https://www.cnblogs.com/blogpro/p/11458305.html
Copyright © 2011-2022 走看看