• 性能测试监控指标及分析调优 | 京东云技术团队一、哪些因素会成为系统的瓶颈?


    1. 什么是MAF和MEF?

    MEF和MEF微软官方介绍:Managed Extensibility Framework (MEF) - .NET Framework | Microsoft Learn

    MEF是轻量化的插件框架,MAF是复杂的插件框架。

    因为MAF有进程隔离和程序域隔离可选。我需要插件进程隔离同时快速传递数据,最后选择了MAF。

    如果不需要真正的物理隔离还是建议使用简单一点的MEF框架。

    2.什么情况下会使用MAF框架?

    例如想做一个浏览器,每点开一个页面都是一个独立的进程,然后进程之间相互隔离。或者多个不同的进程到同一个界面渲染,实现后台进程和界面之间的隔离。

    多进程之间相互传递数据也可以使用MAF。

    3. 如何学习MAF?

    MAF其实是一项很老的技术,入门我看的是《WPF编程宝典》第32章 插件模型。里面有MAF和MEF的详细介绍和许多样例。

    但是要深入理解还是看了很多其他的东西,下面我详细说明,我自己理解和总结的MAF。

    4. MAF框架入门

    4.1 MAF框架构成与搭建

    MAF框架模式是固定的,这里做一个详细介绍。

    首先是要添加几个新项目,下图中不包含主项目。

    Addin文件夹是放置插件用的,其余都是必要项目。

    假设HostView项目和主项目的输出路径是..\Output\

    然后修改每个项目的输出文件夹,例如AddInSideAdapter项目输出路径可以设置为..\Output\AddInSideAdapters\

    注意插件项目输出到Addin文件夹中的子文件夹是..\..\Output\AddIns\MyAddin\

    最后项目的输出文件夹结构是:

    D:\Demo\Output\AddIns

    D:\Demo\Output\AddInSideAdapters

    D:\Demo\Output\AddInViews

    D:\Demo\Output\Contracts

    D:\Demo\Output\HostSideAdapters

    来看看MAF框架模型构成。

     上图中绿色的是被引用蓝色项目所引用。例如HostSideAdapter就要引用Contract和Hostview,如下图所示。

     注意引用时取消勾选复制本地。

     这时就完成基本项目结构的搭建。

    4.2 MAF框架实现

    这里想实现宿主项目和插件项目的双向通信。即插件项目将相关函数接口在宿主实现,然后将宿主项目相关函数接口用委托类的方式注册给插件项目。实现双向通信。

    用《WPF编程宝典》样例代码来说,样例中,插件程序实现ProcessImageBytes处理图像数据的函数,处理同时需要向宿主项目报告处理进度,宿主中 ReportProgress函数实现进度可视化。

    MAF实现一般是先写Contract协议,明确需要的函数接口。然后写AddlnView和HostView。实际上这两个是将函数接口抽象化,在接口里函数复制过来前面加 public abstract 就行。

    之后HostSideAdapter和AddInSideAdapter直接快速实现接口。

    首先从Contract开始,Contract是定义接口,需要设置对象标识符[AddInContract],且必须继承IContract。

     [AddInContract]
        public interface IImageProcessorContract : IContract
        {
            byte[] ProcessImageBytes(byte[] pixels);
    
            void Initialize(IHostObjectContract hostObj);
        }
    
        public interface IHostObjectContract : IContract
        {
            void ReportProgress(int progressPercent);
        }

    Initialize函数是提供宿主函数注册的接口。

     然后在HostView和AddInView分别定义主程序和插件程序的接口抽象类。

    public abstract class ImageProcessorHostView
        {
            public abstract byte[] ProcessImageBytes(byte[] pixels);
    
            public abstract void Initialize(HostObject host);
        }
    
        public abstract class HostObject
        {
            public abstract void ReportProgress(int progressPercent);
        }

    注意AddlnView需要设置对象标识符[AddInBase]。

     [AddInBase]
        public abstract class ImageProcessorAddInView
        {
            public abstract byte[] ProcessImageBytes(byte[] pixels);
    
            public abstract void Initialize(HostObject hostObj);
        }
    
        public abstract class HostObject
        {
            public abstract void ReportProgress(int progressPercent);
        }

    之后在HostSideAdapter实现抽象类。

    注意HostSideAdapter继承HostView的抽象类,在构造函数里需设置ContractHandle插件生存周期,ContractHandle不能为readonly。

      [HostAdapter]
        public class ImageProcessorContractToViewHostAdapter : HostView.ImageProcessorHostView
        {
            private Contract.IImageProcessorContract contract;
            private ContractHandle contractHandle;
    
            public ImageProcessorContractToViewHostAdapter(Contract.IImageProcessorContract contract)
            {            
                this.contract = contract;
                contractHandle = new ContractHandle(contract);
            }              
    
            public override byte[] ProcessImageBytes(byte[] pixels)
            {
                return contract.ProcessImageBytes(pixels);
            }
    
            public override void Initialize(HostView.HostObject host)
            {            
                HostObjectViewToContractHostAdapter hostAdapter = new HostObjectViewToContractHostAdapter(host);
                contract.Initialize(hostAdapter);
            }
        }
    
        public class HostObjectViewToContractHostAdapter : ContractBase, Contract.IHostObjectContract
        {
            private HostView.HostObject view;
    
            public HostObjectViewToContractHostAdapter(HostView.HostObject view)
            {
                this.view = view;
            }
    
            public void ReportProgress(int progressPercent)
            {
                view.ReportProgress(progressPercent);
            }        
        }

    在AddInSideAdapter实现Contract接口,基本和HostSideAdapter类似,只是继承的类不同。

    [AddInAdapter]
        public class ImageProcessorViewToContractAdapter : ContractBase, Contract.IImageProcessorContract
        {
            private AddInView.ImageProcessorAddInView view;
    
            public ImageProcessorViewToContractAdapter(AddInView.ImageProcessorAddInView view)
            {
                this.view = view;
            }
    
            public byte[] ProcessImageBytes(byte[] pixels)
            {
                return view.ProcessImageBytes(pixels);
            }
    
            public void Initialize(Contract.IHostObjectContract hostObj)
            {            
                view.Initialize(new HostObjectContractToViewAddInAdapter(hostObj));            
            }
        }
    
        public class HostObjectContractToViewAddInAdapter : AddInView.HostObject
        {
            private Contract.IHostObjectContract contract;
            private ContractHandle handle;
    
            public HostObjectContractToViewAddInAdapter(Contract.IHostObjectContract contract)
            {
                this.contract = contract;
                this.handle = new ContractHandle(contract);            
            }
                    
            public override void ReportProgress(int progressPercent)
            {
                contract.ReportProgress(progressPercent);
            }
        }

    宿主项目中需要实现HostView里HostObject抽象类。

     private class AutomationHost : HostView.HostObject
            {
                private ProgressBar progressBar;
                public AutomationHost(ProgressBar progressBar)
                {
                    this.progressBar = progressBar;
                }
                public override void ReportProgress(int progressPercent)
                {
                    // Update the UI on the UI thread.
                    progressBar.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
                        (ThreadStart)delegate()
                    {
                        progressBar.Value = progressPercent;
                    }
                    );                
                }
            }

    然后是在宿主项目里激活插件,并初始化AutomationHost。

    string path = Environment.CurrentDirectory;            
    AddInStore.Update(path);//更新目录中Addins目录里的插件
    IList tokens = AddInStore.FindAddIns(typeof(HostView.ImageProcessorHostView), path);//查找全部插件
    lstAddIns.ItemsSource = tokens;//插件可视化
    AddInToken token = (AddInToken)lstAddIns.SelectedItem;//选择插件
    AddInProcess addInProcess = new AddInProcess();//创建插件进程
    addInProcess.Start();//激活插件进程
    addin = token.Activate(addInProcess,AddInSecurityLevel.Internet);//激活插件
    //如果只是想隔离程序域,就无需创建AddInProcess,激活插件如下
    // HostView.ImageProcessorHostView addin = token.Activate(AddInSecurityLevel.Host);
    automationHost = new AutomationHost(progressBar);//创建AutomationHost类
    addin.Initialize(automationHost);//初始化automationHost

     插件项目中实现AddInView中的抽象类。

    [AddIn("Negative Image Processor", Version = "1.0", Publisher = "Imaginomics",Description = "")]
        public class NegativeImageProcessor : AddInView.ImageProcessorAddInView 
        {
            public override byte[] ProcessImageBytes(byte[] pixels)
            {
                int iteration = pixels.Length / 100;         
                for (int i = 0; i < pixels.Length - 2; i++)
                {
                    pixels[i] = (byte)(255 - pixels[i]);
                    pixels[i + 1] = (byte)(255 - pixels[i + 1]);
                    pixels[i + 2] = (byte)(255 - pixels[i + 2]);
                    if (i % iteration == 0)
                    {
                        host?.ReportProgress(i / iteration);
                    }
                }
                return pixels;
            }
    
            private AddInView.HostObject host;
            public override void Initialize(AddInView.HostObject hostObj)
            {
                host = hostObj;
            }

     这时宿主可以把数据传递给插件程序,插件程序中ProcessImageBytes处理数据然后通过host?.ReportProgress(i / iteration);向宿主传递消息。

    这里有提供样例程序。

    项目附件.7z

    5. MAF框架常见问题

    5.1手动关闭插件

    AddInController addInController = AddInController.GetAddInController(addIn);
    addInController.Shutdown();

    此方法适应于非应用隔离的手动关闭。对于应用隔离式插件,用此方法会抛出异常。

    如上面样例就是应用隔离的插件,可以根据进程id直接关闭进程。

    public void ProcessClose()
            {
                try
                {
                    if (process != null)
                    {
                        Process processes = Process.GetProcessById(addInProcess.ProcessId);
                        if (processes?.Id > 0)
                        {
                            processes.Close();
                        }
                    }
                }
                catch (Exception)
                {
                }5.2 插件异常
    

    System.Runtime.Remoting.RemotingException: 从 IPC 端口读取时失败: 管道已结束。这是插件最常见的异常,因为插件抛出异常而使得插件程序关闭。

    如果是插件调用非托管代码,而产生的异常,可以查Windows应用程序日志来确定异常。其余能捕获的异常尽量捕获保存到日志,方便查看。

    5.3 双向通信

    实际应用过程中,往往是通过委托来将宿主相关函数暴露給一个类,然后通过在宿主程序初始化后。在插件中实例化后就可以直接调用宿主的相关函数,反之同理。

    这里是通过委托暴露宿主的一个函数。

    public delegate void UpdateCallBack(string message, bool isclose, int leve);
        public class VideoHost : HostAddInView
        {
            public event UpdateCallBack Updatecallback;
            public override void ProcessVideoCallBack(string message, bool isclose, int leve)
            {
                Updatecallback?.Invoke(message, isclose, leve);
            }
        }

     在插件程序中实例化后调用。

    private HostAddInView hostAddInView;
            public override void Initialize(HostAddInView hostAddInView)
            {
                this.hostAddInView = hostAddInView;
            }
            private void ErrorCallback(string message, bool isclose, int leve)
            {
                hostAddInView?.ProcessVideoCallBack(message, isclose, leve);
            }

    6. MAF深入理解

     MAF本质是实现IpcChannel通信,在一个期刊中有作者抛弃MAF固定结构自己实现IpcChannel,因为代码很复杂,就不在此详细阐述。

    如果要实现应用域隔离,自己实现IpcChannel,MAF中的应用域隔离实现也是非常好的参考资料。

    MAF的7层结构主要是实现从插件的宿主函数转换,例如可以在将插件程序的界面放入主界面中渲染,做出像浏览器一样的开一个界面就是一个进程。将插件中的组件在AddInSideAdapter中转换为Stream然后在HostSideAdapter中将Stream实例化为组件。而HostView和AddInView实际上是提供两个转换接口,Contract是定义传输接口。

    另外如果传输插件向数组传递图像数据,最后是转换成byte[],或者使用共享内存。 

  • 相关阅读:
    【QT HTTP】使用QtNetwork模块制作基于HTTP请求的C/S架构
    find /root -type f -mtime +30 -exec rm {} ;的含义
    软件质量保护与测试(第2版)学习总结第十章 黑盒测试
    什么叫渗透测试爆破
    (二十三)数据结构-交换排序
    怎么优化Web网站性能
    每日一博 - 闲聊SQL Query Execution Order
    Spider爬虫入门(发送Get Post请求、携带请求头、Cookie、Session、响应Response、获取二进制数据 、解析Json数据)
    Dubbo+Zookeeper
    用QT实现一个简单的计算机
  • 原文地址:https://blog.csdn.net/2201_75761617/article/details/133043740