• 如何证明Application Domain的隔离性


    Application Domain可以看作是一个Assembly的逻辑容器。在程序执行过程中,如果遇到需要的Type并没有定义在已经加载的Assemblies中,CLR会把相应的Assembly加载的该Application Domain中。每个Application Domain都有一个属于自己的加载器堆(Loader Heap),用于维护从Application Domain创建以后所用到的所有的Type,以及这些Type对应的方法表——维护这样一个Mapping:定义在Type中的所有方法和经过JIT编译后x86代码(只考虑32bit处理器)。

    Application Domain之间是相互隔离,互不干扰。在一个Application Domain创建的对象不能被另一个Application Domain直接调用,反映在内存分配上面——就是各个Application Domain使用各个独立的内存地址空间。一个对象根据他所对应的类型(如System.MarshalByRefObject通过传递引用的方式)或者属性(比如对于定义了System. SerializableAttribute的Type采用传递值得方式)以两种不同的方式在Application Domain之间传递——By Reference 和By Value。

    这些都是地球人都知道的.NET的基本原理,但是相信很多人没有尝试过通过Coding的方式证明这种机制。

    那么现在我们就先来看看我们的Sample: 


    using System;
    using System.Collections.Generic;
    using System.Text;
    using System.Runtime.Remoting;
    using System.Reflection;
    using Artech.AppDomainIsolation;

    namespace Artech.AppDomainIsolation
    {
        
    public class GeneralType
        
    {
            AppDomain GetAppDomain()
            
    {
                
    return AppDomain.CurrentDomain;
            }

        }


        [Serializable]
        
    public class MarshalByValueType
        
    {
            
    public AppDomain GetAppDomain()
            
    {
                
    return AppDomain.CurrentDomain;
            }

        }


        
    public class MarshalByRefType : MarshalByRefObject
        
    {
            
    public AppDomain GetAppDomain()
            
    {
                
    return AppDomain.CurrentDomain;
            }

        }

         
    class Program
        
    {
             
            
    static void Main(string[] args)
            
    {
                AppDomain appDomain 
    = AppDomain.CreateDomain("newAppDomain");

                
    string assemblyName = "Artech.AppDomainIsolation";
                
    try
                
    {
                    GeneralType generalObject 
    = (GeneralType)appDomain.CreateInstanceAndUnwrap(assemblyName, "Artech.AppDomainIsolation.GeneralType");
                }

                
    catch (Exception ex)
                
    {
                    Console.WriteLine(
    "Fail to pass a general type instance created in another application domain to default appliction domain,beacuse \"{0}\"", ex.Message);
                }


                MarshalByValueType marshalByValueObject 
    = (MarshalByValueType)appDomain.CreateInstanceAndUnwrap(assemblyName, "Artech.AppDomainIsolation.MarshalByValueType");
                MarshalByRefType marshalByRefObject 
    = (MarshalByRefType)appDomain.CreateInstanceAndUnwrap(assemblyName, "Artech.AppDomainIsolation.MarshalByRefType");

                Console.WriteLine(
    "\nmarshalByValueObject is a transparent proxy? {0}.",RemotingServices.IsTransparentProxy(marshalByValueObject)?"Yes":"No");
                Console.WriteLine(
    "marshalByRefObject is a transparent proxy? {0}.", RemotingServices.IsTransparentProxy(marshalByRefObject) ? "Yes" : "No");

                AppDomain appDomain_MarshalByValueType 
    = marshalByValueObject.GetAppDomain();
                AppDomain appDomain_MarshalByRefType 
    = marshalByRefObject.GetAppDomain();

                Console.WriteLine(
    "\nThe method of marshalByValueObject is executed in the default application domain?\"{0}\"",
                    
    object.ReferenceEquals(AppDomain.CurrentDomain, appDomain_MarshalByValueType) ? "Yes" : "No");
                Console.WriteLine(
    "The method of marshalByRefObject is executed in the default application domain?\"{0}\"",
                   
    object.ReferenceEquals(AppDomain.CurrentDomain, appDomain_MarshalByRefType) ? "Yes" : "No");

                AppDomain.Unload(appDomain_MarshalByRefType);
                marshalByValueObject.GetAppDomain();

                
    try
                
    {
                    marshalByRefObject.GetAppDomain();
                }

                
    catch (Exception ex)
                
    {
                    Console.WriteLine(
    "\nAn exception is thorwn when calling the method of marshalByRefObject because \"{0}\"", ex.Message);
                }

                Console.Read();
            }

        }

    }


    下面是运行结果的Screen Shot。


    接下来我们来分析这段代码。

    1. 我们首先定义个3个不同的Type,他们都一个相同的方法——GetAppDomain()用于获取执行该方法的真正的Application Domain。

    • GeneralType:一般的Type, 没有什么特别。
    • MarshalByValueType:定义了一个System. SerializableAttribute(你也可以通过使它实现System.Runtime.Serialization.ISerializable Interface来模拟这个Sample),它将以By Value的方式在不同的Application Domain之间传递。
    • MarshalByRefType:继承自System. MarshalByRefObject。该类型的对象它将以By Reference的方式在不同的Application Domain之间传递。
    public class GeneralType
        
    {
            AppDomain GetAppDomain()
            
    {
                
    return AppDomain.CurrentDomain;
            }

        }


        [Serializable]
        
    public class MarshalByValueType
        
    {
            
    public AppDomain GetAppDomain()
            
    {
                
    return AppDomain.CurrentDomain;
            }

        }


        
    public class MarshalByRefType : MarshalByRefObject
        
    {
            
    public AppDomain GetAppDomain()
            
    {
                
    return AppDomain.CurrentDomain;
            }

     }

    2. 在Main()中我们首先创建一个新的Application Domain,并为他指定一个Friendly Name——newAppDomain。

    AppDomain appDomain = AppDomain.CreateDomain("newAppDomain");

    3. 接着我们分别在这个新建的Appliation Domain中创建我们在1中定义的3个类型的对象——generalObject,marshalByValueObject,marshalByRefObject,并试着把它传递到当前的Application Domain——Default Application Domain。从运行的结果我们可以看出,当我们传递generalObject的时候,一个Exception被抛出,从Error Message可以开出原因——“Type 'Artech.AppDomainIsolation.GeneralType' in assembly 'Artech.AppDomainIsolation, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null' is not marked as serializable.”从而我们可以看出,一般的Type是不能在不同的Application之间传递的。

    try
                
    {
                    GeneralType generalObject 
    = (GeneralType)appDomain.CreateInstanceAndUnwrap(assemblyName, "Artech.AppDomainIsolation.GeneralType");
                }

                
    catch (Exception ex)
                
    {
                    Console.WriteLine(
    "Fail to pass a general type instance created in another application domain to default appliction domain,beacuse \"{0}\"", ex.Message);
                }


                MarshalByValueType marshalByValueObject 
    = (MarshalByValueType)appDomain.CreateInstanceAndUnwrap(assemblyName, "Artech.AppDomainIsolation.MarshalByValueType");
                MarshalByRefType marshalByRefObject 
    = (MarshalByRefType)appDomain.CreateInstanceAndUnwrap(assemblyName, "Artech.AppDomainIsolation.MarshalByRefType");

    4. 通过3,证明了标记的System. SerializableAttribute属性和继承自System. MarshalByRefObject的Type对应的对象是可以在不同的Application Domain之间传递呢。但是他们之间又会有怎样的差异呢?他们是真正的对象呢?还仅仅是位于新建Application Domain中的对象一个代理(Proxy)?我们通过调用定义在System.Runtime.Remoting中的静态方法: IsTransparentProxy。了解Remoting的人相信对这个方法不会感到陌生。他用于判断某个对象是否是一个Transparent Proxy(在当我们跨Application Domain远程调用一个Remote Object的时候,实际上我们并非直接调用Remote Object的方法,而是通过一个同Client处在同一个Application Domain的Transparent Proxy对象间接地调用远程对象——Transparent Proxy具有一个Remote Object的Reference,可以轻易地找到这个Remote Object。其实在真正的场景中,Client调用Transparent Proxy,Transparent Proxy再去调用Real Proxy,Real Proxy最终才去调用Remote Object——如果你想进一步地了解Remoting,你可以参照MSDN)

    Console.WriteLine("\nmarshalByValueObject is a transparent proxy? {0}.",RemotingServices.IsTransparentProxy(marshalByValueObject)?"Yes":"No");
                Console.WriteLine(
    "marshalByRefObject is a transparent proxy? {0}.", RemotingServices.IsTransparentProxy(marshalByRefObject) ? "Yes" : "No");

    通过运行结果,我们可以看到:MarshalByValueType对象marshalByValueObject,IsTransparentProxy方法返回False。而对于MarshalByRefType对象marshalByRefObject则返回True。这就充分证明了,标记了System. SerializableAttribute属性的Type所对应的对象是一个真正意义上的对象,而对于继承自MarshalByRefObject Type,当该Type对应的对象从一个Application Domain传递到另一个Application Domain后,另一个Application Domain获得的仅仅是原来对象的Proxy而已。

    我们可以从传递的机制来解释这种差异。当你把System. SerializableAttribute属性运用要某个Type,或者让某个Class实现System.Runtime.Serialization.ISerializable Interface的时候,你实际上是给该Type赋予了一种能力——一种可以序列化成XML的能力(XMLSerializer负责把对象序列化成XML)。当这种对象从一个Application Domain传递到另一个Application Domain的时候,Object先被序列化成XML,接着把XML传递到另一个Application Domain中,在新的Application Domain中,通过反序列化重新生成一个新的Object——这个新的Object和处于另一个Application Domain已经没有任何关系。

    而对于而对于继承自MarshalByRefObject Type的对象,虽然他不能够序列化成XML,但是可以通过传递Reference的方式在Application Domain之间 传递。当这种传递实质上是通过在另一个Application创建一个拥有该对象引用的一个Proxy。而这个Proxy依赖一这个处理另一个Application Domain的真正对象。

    5. 上面我们实际上已经说清楚了两个对象传递的差异——By Value 和By Reference。现在我们来进一步验证前面我们说的。我们继续来看看我们的代码:

    AppDomain appDomain_MarshalByValueType = marshalByValueObject.GetAppDomain();
    AppDomain appDomain_MarshalByRefType 
    = marshalByRefObject.GetAppDomain();

                Console.WriteLine(
    "\nThe method of marshalByValueObject is executed in the default application domain?\"{0}\"",
                    
    object.ReferenceEquals(AppDomain.CurrentDomain, appDomain_MarshalByValueType) ? "Yes" : "No");
                Console.WriteLine(
    "The method of marshalByRefObject is executed in the default application domain?\"{0}\"",
                   
    object.ReferenceEquals(AppDomain.CurrentDomain, appDomain_MarshalByRefType) ? "Yes" : "No");

    我们通过调用连个对象的GetAppDomain()方法,从而获得真正执行该方法的Application Domain。然后再和当前的Application Domain进行比较。我们发现对于MarshalByValueType对象marshalByValueObject,真正执行操作是在当前的Application Domain中进行的。而对于MarshalByRefType对象marshalByRefObject,则是我们新建立的Application Domain。这充分证明了当marshalByValueObject传递到新的Application后,生成一个和原来对象一模一样的对象,这个对象具有执行自身操作的能力。而对于marshalByRefObject,由于它只是一个Proxy而已,他只有把对对象的操作请求发送给真正的,位于另一个Application Domain的真正对象(同时他也负责把远程对象的执行结果返回给调用者),真正的操作实际上是发生在远程对象的Application Domain。

    由于marshalByRefObject,依赖于远程对象,所以当我们卸载掉Host远程对象的Application Domain,对marshalByRefObject的任何调用将变得无效。所以这里可以对下面这段代码作出解释。

    AppDomain.Unload(appDomain_MarshalByRefType);
    marshalByValueObject.GetAppDomain();

                
    try
                
    {
                    marshalByRefObject.GetAppDomain();
                }

                
    catch (Exception ex)
                
    {
                    Console.WriteLine(
    "\nAn exception is thorwn when calling the method of marshalByRefObject because \"{0}\"", ex.Message);
                }

    注:在分布式开发中,我们会大量接触到By Value 和By Refernce传递。一般而言,By Value用于商业实体的传递(Business Entity),而By Reference用于远程调用(RPC)或者是调用Service。

    作者:Artech
    出处:http://artech.cnblogs.com/
    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。
  • 相关阅读:
    vue2.0开发聊天程序(八) 初步完成
    王下邀月熊_Chevalier的前端每周清单系列文章索引
    将HTML页面转换为PDF文件并导出
    二维码活码管理系统
    前端眼里的docker
    这些好玩的例子,希望你也能喜欢
    如何实现swipe、tap、longTap等自定义事件
    基于 HTML5 Canvas 的交互式地铁线路图
    【php学习】时间函数
    页面瀑布流布局的实现 javascript+css
  • 原文地址:https://www.cnblogs.com/zhangchenliang/p/2621972.html
Copyright © 2020-2023  润新知