在新AppDomain中建立的第一个WCF连接非常慢

我有一个我使用的库,它使用WCF调用http服务来获取设置。 通常,第一次调用需要约100毫秒,后续调用只需几毫秒。 但是我发现当我创建一个新的AppDomain时,来自该AppDomain的第一个WCF调用需要2.5秒。

有没有人解释或解决为什么在新的AppDomain中首次创建WCF频道需要这么长时间?

这些是基准测试结果(当在64位版本中没有附带调试器的情况下运行时),请注意第二组数字中的第一个连接如何延长25倍以上

Running in initial AppDomain First Connection: 92.5018 ms Second Connection: 2.6393 ms Running in new AppDomain First Connection: 2457.8653 ms Second Connection: 4.2627 ms 

这不是一个完整的例子,但显示了我生成这些数字的大部分内容:

 class Program { static void Main(string[] args) { Console.WriteLine("Running in initial AppDomain"); new DomainRunner().Run(); Console.WriteLine(); Console.WriteLine("Running in new thread and AppDomain"); DomainRunner.RunInNewAppDomain("test"); Console.ReadLine(); } } class DomainRunner : MarshalByRefObject { public static void RunInNewAppDomain(string runnerName) { var newAppDomain = AppDomain.CreateDomain(runnerName); var runnerProxy = (DomainRunner)newAppDomain.CreateInstanceAndUnwrap(typeof(DomainRunner).Assembly.FullName, typeof(DomainRunner).FullName); runnerProxy.Run(); } public void Run() { AppServSettings.InitSettingLevel(SettingLevel.Production); var test = string.Empty; var sw = Stopwatch.StartNew(); test += AppServSettings.ServiceBaseUrlBatch; Console.WriteLine("First Connection: {0}", sw.Elapsed.TotalMilliseconds); sw = Stopwatch.StartNew(); test += AppServSettings.ServiceBaseUrlBatch; Console.WriteLine("Second Connection: {0}", sw.Elapsed.TotalMilliseconds); } } 

对AppServSettings.ServiceBaseUrlBatch的调用是创建服务的通道并调用单个方法。 我使用wireshark来观看呼叫,从服务获得响应只需要一毫秒。 它使用以下代码创建通道:

 public static ISettingsChannel GetClient() { EndpointAddress address = new EndpointAddress(SETTINGS_SERVICE_URL); BasicHttpBinding binding = new BasicHttpBinding { MaxReceivedMessageSize = 1024, OpenTimeout = TimeSpan.FromSeconds(2), SendTimeout = TimeSpan.FromSeconds(5), ReceiveTimeout = TimeSpan.FromSeconds(5), ReaderQuotas = { MaxStringContentLength = 1024}, UseDefaultWebProxy = false, }; cf = new ChannelFactory(binding, address); return cf.CreateChannel(); } 

通过分析应用程序,它显示在第一种情况下构建通道工厂并创建通道并调用该方法需要不到100毫秒

在新的AppDomain构建通道工厂花了763毫秒,创建通道521毫秒,在接口上调用方法1,098毫秒。

TestSettingsRepoInAppDomain.DomainRunner.Run()2,660.00 TestSettingsRepoInAppDomain.AppServSettings.get_ServiceBaseUrlBatch()2,543.47 Tps.Core.Settings.Retriever.GetSetting(string,!! 0,!! 0,!! 0)2,542.66 Tps.Core.Settings.Retriever.TryGetSetting (string,!! 0&)2,522.03 Tps.Core.Settings.ServiceModel.WcfHelper.GetClient()1,371.21 Tps.Core.Settings.ServiceModel.IClientChannelExtensions.CallWithRetry(class System.ServiceModel.IClientChannel)1,098.83

编辑

在使用带有.NET CLR Loading对象的perfmon之后,我可以看到,当它加载第二个AppDomain时,它会将更多的类加载到内存中,而不是最初。 第一个平面线是我在第一个appdomain之后放置的暂停,它加载了218个类。 第二个AppDomain导致加载1,944个类。

我假设所有这些类的加载都占用了所有的时间,所以现在的问题是,它加载了什么类,为什么?

在此处输入图像描述

UPDATE

答案结果是因为只有一个AppDomain能够利用原生图像系统dll。 所以第二个appdomain的缓慢是它必须重新调用wcf使用的所有System。* dll。 第一个appdomain可以使用这些dll的预先定义的本机版本,因此它没有相同的启动成本。

在调查了Petar建议的LoaderOptimizationAttribute之后,确实似乎解决了这个问题,使用MultiDomain或MultiDomainHost会导致第二个AppDomain花费的时间与第一次通过wcf访问内容的时间相同

在这里你可以看到默认选项,注意在第二个AppDomain中如何没有任何程序集说Native,这意味着它们都必须被重新安装,这就是所有的时间

在此处输入图像描述

这是在向Main添加LoaderOptimization(LoaderOptimization.MultiDomain)之后。 您可以看到所有内容都已加载到共享AppDomain中

在此处输入图像描述

这是用户LoaderOptimization(LoaderOptimization.MultiDomainHost)之后的main。 您可以看到所有系统dll都是共享的,但是我自己的dll和GAC中没有的dll会单独加载到每个AppDomain中

在此处输入图像描述

因此,对于使用MultiDomainHost提示此问题的服务是答案,因为它具有快速启动时间,我可以卸载AppDomains以删除服务使用的动态构建的程序集

您可以使用LoaderOptimization属性修饰Main,以告诉CLR加载器如何加载类。

 [LoaderOptimization(LoaderOptimization.MultiDomain)] MultiDomain - Indicates that the application will probably have many domains that use the same code, and the loader must share maximal internal resources across application domains. 

你在IE中定义了HTTP代理吗? (也许是一个自动配置脚本)。 这可能是一个原因。

否则我猜它是加载所有dll所需的时间。 尝试将代理创建从actull调用到服务,以查看花费时间的内容。

我发现以下文章讨论了第一个AppDomain如何才能使用本机映像dll,因此子appdomain将永远被强制JIT许多初始AppDomain不具备的东西。 这可能会导致我看到的性能影响,但是有可能以某种方式不能获得性能损失吗?

如果程序集有本机映像,则只有第一个AppDomain可以使用本机映像。 所有其他AppDomain都必须JIT编译代码,这可能导致显着的CPU成本。