使用 JointCode.Shuttle 提高跨 AppDomain 通信的性能

JointCode.Shuttle 是一个用于进程内 AppDomain 间通信的服务架构(不支持跨进程),它旨在取代运行时库提供的 MarshalByrefObject 的功能。

本文比较了 MarshalByrefObject 与 JointCode.Shuttle 的跨 AppDomain 对象访问的性能。

背景知识

我们知道跨 AppDomain 调用的性能是十分低下的,因为在这种情况下对象之间的交互不是直接的,而是要穿越一个边界 (AppDomain)。

为了穿越这个边界,首先需要将对象转换(术语称为“marshal”,即封送)成一个可以传输的形态(比如,在 .NET Remoting 中对象将被打包成一个可序列化的 ObjRef 实例——MarshalByrefObject 中的 ByRef 指的就是 ObjRef 这种形态);然后,当数据流到达目标 AppDomain 时,还要执行解封 (unmarshal) 操作将其还原为内存中的对象。这个过程是非常耗时的,因为其中可能涉及到各种反射、序列化/反序列化、安全检查等等。可以想象得到,如果一个程序频繁发起跨 AppDomain 调用,那么它的性能恐怕将会受到严重影响。

好在我们一般不需要和 AppDomain 打交道,因此也不必太多关注它。然而,有的时候我们确实不得不使用 AppDomain。比如说在插件环境下,我们有可能需要动态加载/卸载程序集,在这种情况下 AppDomain 是不二选择,因为我们知道在 .net/mono 中,程序集是不能单独卸载的,只能随着 AppDomain 一起卸载。

为了提高 AppDomain 间通信的性能,作者开发了 JointCode.Shuttle。

测试

为了比较 JointCode.Shuttle 和 MarshalByrefObject 的跨 AppDomain 调用性能,并且尽量了解可能会影响性能的各个因素,我编写了一个测试。该测试向我们展示了两种类型的结果:

  • 反复创建远程服务对象并调用对象方法:这是为了从整体上比较二者的调用性能(包括创建对象和调用方法的开销)。
  • 创建远程服务对象,并将其缓存到一个本地字段中,然后反复调用对象方法:这是单纯为了比较二者的远程方法调用性能(仅仅测试调用方法的开销)。因为方法调用时可能需要传递各种不同类型和数量的参数,这些都会影响最终性能。需要特别指出的是,这种做法仅仅是为了方便测试,在实践中是不能将远程服务对象的引用缓存到字段的。因为远程服务对象的生命期受控于远端,它有可能在远端已经超时失效并被垃圾回收,而本地调用者对此并不知情,仍旧继续调用其方法,此时将会抛出异常。

 

下面这张图是部分测试结果:

 JointCode.Shuttle与MarshalByObject性能对比

如果您对测试代码本身感兴趣,可移步前往 此处 下载测试源码(测试名称:ShuttleDomain性能测试)。

posted @ 2017-07-18 13:30  Johnny.Liu  阅读(413)  评论(0编辑  收藏  举报