在现代软件开发中,应用程序域(AppDomain)是一种用于隔离应用程序代码执行环境的技术。然而,跨应用程序域的通信往往会导致性能问题。研究表明,跨应用程序域的方法调用比同一应用程序域内的正常方法调用慢数百至数千倍。这是因为当对象从一个执行边界(AppDomain)传递到另一个时,它需要被转换成可以传输的形式,然后在到达后重新构建并转换回原始形式,这个过程被称为“封送”。例如,在.NET Remoting中,派生自MarshalByRefObject的对象会被打包成一个可序列化的ObjRef实例,这就是类型名称中“ByRef”的含义,它指的是ObjRef的形式。
封送过程会产生大量的开销,如反射、安全检查、序列化/反序列化等。由于涉及的操作众多,性能损失是不可避免的。因此,如果关心性能,通常建议避免使用应用程序域。但是,有时确实需要使用另一个应用程序域,例如当实现插件架构并需要在运行时加载和卸载程序集而不停止应用程序时。在这种情况下,一个快速的跨应用程序域封送器会非常有用。这就是JointCode.Shuttle的用武之地。
JointCode.Shuttle的分发包包含两个文件:JointCode.Shuttle.dll和JointCode.Shuttle.Library.dll,其中JointCode.Shuttle.dll是用托管语言编写的库,而JointCode.Shuttle.Library.dll是用非托管语言编写的组件,前者依赖于后者。
要使用JointCode.Shuttle,首先需要在项目中引用JointCode.Shuttle.dll,并将JointCode.Shuttle.Library.dll复制到JointCode.Shuttle.dll编译的文件夹中(例如,如果JointCode.Shuttle.dll被复制到c:/projects/sampleproject文件夹中,需要手动将JointCode.Shuttle.Library.dll复制到这个文件夹中)。
JointCode.Shuttle是面向接口的,所以首先需要创建一个服务接口(也称为服务契约),并应用ServiceInterface属性。
public interface IServiceFunctionTest
{
// 属性和方法...
}
然后创建一个实现该契约的服务类,并应用ServiceClass属性。
[ServiceClass(typeof(IServiceFunctionTest), Lifetime = LifetimeEnum.Transient)]
public class ServiceFunctionTest : IServiceFunctionTest
{
// 属性和方法
}
因为想要进行跨应用程序域调用,需要编写一个类来启动/停止远程服务,并让它继承自MarshalByRefObject。
public abstract class RemoteServiceEnd : MarshalByRefObject
{
protected ShuttleDomain _shuttleDomain;
public void CreateShuttleDomain()
{
var key = this.GetType().Name;
_shuttleDomain = ShuttleDomainHelper.Create(key, key);
}
public void DisposeShuttleDomain()
{
if (_shuttleDomain != null)
_shuttleDomain.Dispose();
}
public abstract void RegisterServices();
public abstract void ConsumeServices();
}
现在,已经准备好使用JointCode.Shuttle了。首先,必须初始化ShuttleDomain。初始化操作可以在默认应用程序域或其他应用程序域中进行,但必须在创建任何ShuttleDomain实例之前完成。
ShuttleDomain.Initialize();
AppDomain _remoteDomain = AppDomain.CreateDomain(Guid.NewGuid().ToString(), null, null);
RemoteServiceEnd _serviceEnd1 = (RemoteServiceEnd)_remoteDomain.CreateInstanceAndUnwrap(_serviceEnd1AsmName, ServiceEnd1Type);
_serviceEnd1.CreateShuttleDomain();
var key = Guid.NewGuid().ToString();
_shuttleDomain = ShuttleDomainHelper.Create(key, key);
_serviceEnd1.RegisterServices();
_shuttleFunctionTest.CallSimpleMethod();
为了比较JointCode.Shuttle和MarshalByrefObject的跨应用程序域调用性能,并尝试理解可能影响性能的各种因素,构建了一个测试。这个测试向展示了两种基准测试:
请注意,这只是为了方便测试,实际中永远不要将远程服务对象缓存到字段中。因为远程服务对象的生命周期由远程端控制,有可能远程对象已经过期并被远程端的垃圾回收器回收,而本地调用者却不知道。在这种情况下,如果继续调用其方法,将抛出异常。