.net平台的AppDomain技术实现服务的重启功能

  • 一直很好奇电脑上运行的服务重启是怎么实现的?
  • 了解到.net平台的AppDomain技术后有了新的思路(之前想到的重启就是自己关了自己,然后再启动自己(实现代码);还有就是用“宿组”程序关闭服务再启动服务(有点套娃的意思了))。
  • 本Demo(winform程序)借用AppDomain实现服务的重启功能(本质:服务核心dll跑在程序域里,重启就是卸载再加载过程),参考了此博客,借助本技术可以实现插件化编程。
using System;
using System.Reflection;
using System.Threading;
using System.Windows.Forms;

namespace MyService
{
    public partial class Form1 : Form
    {
        AppDomain serviceDomain = null;
        public Form1()
        {
            InitializeComponent();
            ThreadPool.QueueUserWorkItem(new WaitCallback(Run), "");//执行服务
            textBox3.Text = @"服务启动和重启Demo,介绍:

(1)、借用.net平台的AppDomain技术实现服务的重启功能(本质:服务核心dll跑在程序域里,重启就是卸载再加载过程)

(2)、每次重启都新建程序域,故通过程序域名识别是否重新加载成功。";
        }

        private void button1_Click(object sender, EventArgs e)
        {
            textBox1.Text = "";
        }

        /// <summary>
        /// 重启
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void button2_Click_1(object sender, EventArgs e)
        {
            ThreadPool.QueueUserWorkItem(new WaitCallback(Run), "");
        }

        #region MyRegion

        private void Run(object v)
        {
            if (serviceDomain != null)
            {
                Action ac = () =>
                {
                    textBox2.Text = "重启中...";
                };
                this.Invoke(ac);
                AppDomain.Unload(serviceDomain);//卸载程序域
				Thread.Sleep(3000);
                Action ac2 = () =>
                {
                    textBox2.Text = "重启成功";
                };
                this.Invoke(ac2);
            }
            serviceDomain = AppDomain.CreateDomain("AppDomain" + DateTime.Now.ToString("ddHHmmss"), null, null); //创建子Domain
            ProxyObject myService = serviceDomain.CreateInstanceAndUnwrap(typeof(ProxyObject).Assembly.FullName, "MyService.ProxyObject") as ProxyObject;
            myService.LoadAssembly();
            myService.ReturnVal += EventAction;
            myService.ExecWhile("AppDomainPlugin.MyTest", "Abc", "test");
        }

        /// <summary>
        /// 在界面显示结果
        /// </summary>
        /// <param name="a"></param>
        private void EventAction(object a)
        {
            Action ac = () =>
            {
                textBox1.Text += serviceDomain.FriendlyName + " = " + a.ToString() + "\r\n";
            };
            this.Invoke(ac);
        } 
        #endregion
    }

    #region

    // 该类的实例可跨越AppDomain的边界"按引用封送"
    class ProxyObject : MarshalByRefObject
    {
        public event Action<object> ReturnVal;
        Assembly assembly = null;
        public void LoadAssembly()
        {
            assembly = Assembly.LoadFile(@"C:\Users\aj\Documents\Visual Studio 2015\Projects\MyService\AppDomainPlugin\bin\Debug\AppDomainPlugin.dll");
        }

        /// <summary>
        /// 执行指定dll中方法
        /// </summary>
        /// <param name="fullClassName">类全名(命名空间.类名,如:AppDomainPlugin.MyTest)</param>
        /// <param name="methodName">方法名</param>
        /// <param name="args">方法入参</param>
        /// <returns></returns>
        public object ExecFun(string fullClassName, string methodName, params Object[] args)
        {
            if (assembly == null)
                return false;
            Type tp = assembly.GetType(fullClassName);
            if (tp == null)
                return false;
            MethodInfo method = tp.GetMethod(methodName);
            if (method == null)
                return false;
            object newObj = Activator.CreateInstance(tp);//反射new一个实例对象
            return method.Invoke(newObj, args);//执行指定的方法并返回
        }

        /// <summary>
        /// 轮询方式 执行指定dll中方法
        /// </summary>
        /// <param name="fullClassName">类全名(命名空间.类名,如:AppDomainPlugin.MyTest)</param>
        /// <param name="methodName">方法名</param>
        /// <param name="args">方法入参</param>
        /// <returns></returns>
        public void ExecWhile(string fullClassName, string methodName, params object[] args)
        {
            Type tp = assembly.GetType(fullClassName);
            MethodInfo method = tp.GetMethod(methodName);
            object newObj = Activator.CreateInstance(tp);//new一个实例对象
            while (true)
            {
                //%5是0来模拟触发事件时机
                if (DateTime.Now.Second % 5 == 0)
                {
                    var a = method.Invoke(newObj, args);//执行指定的方法并返回
                    if (a != null)
                    {
                        ReturnVal(a);//执行事件
                    }
                }
            }
        }
    }
    #endregion
} 
  • 用AppDomainPlugin模拟服务核心dll(重启就卸载和重载本dll)
using System;

namespace AppDomainPlugin
{
    public class MyTest
    {
        public string Abc(string a)
        {
            return a + "  " + DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        }
    }
}

应用程序域,目的就是提供隔离性(一个域失败,其他不受影响)。借用此技术程序集可单独加载(加载相同程序集的不同版本)、动态卸载。

  • .Net 进程中多个应用程序域(AppDomain)会影响性能吗? —— 反射加载dll不用停止进程,但是卸载、运行中替换的话也要停止进程。
  • 1个进程 = n个应用程序域1个应用程序域 = n个线程
  • 应用程序域原则上相互独立,用“socket、.net Remoting、MarshalByRefObject”等通信
  • AppDomain实现程序集热插拔、异常隔离等等。但 AppDomain不好用,最大的问题就是跨 AppDomain 调用的性能,让人望而却步的。专门针对跨 AppDomain 通信的库JointCode.Shuttle

posted on 2022-01-08 16:05  anjun_xf  阅读(132)  评论(0编辑  收藏  举报

导航

TOP