2012-04-13 55 views
16

我有一个使用WCF调用http服务来获取设置的库。通常情况下,第一次调用需要大约100毫秒,随后的调用只需要几毫秒。但是我发现,当我创建一个新的AppDomain时,来自该AppDomain的第一个WCF调用需要超过2.5秒。在新的AppDomain中创建的第一个WCF连接非常缓慢

有没有人有解释或修复为什么第一次创建一个新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<ISettingsChannel>(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(字符串,!! 0,!! 0,!! 0)2,542.66 Tps内。 Core.Settings.Retriever.TryGetSetting(字符串,!! 0 &)2,522.03 Tps.Core.Settings.ServiceModel.WcfHelper.GetClient()1,371.21 Tps.Core.Settings.ServiceModel.IClientChannelExtensions.CallWithRetry(类System.ServiceModel.IClientChannel )1,098.83

编辑

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

我认为它的所有这些类正在加载所有的时间,所以现在的问题是,它加载了什么类,为什么?

enter image description here

UPDATE

答案原来是因为事实上只有一个AppDomain中能够采取机映像系统DLL的优势。所以第二个appdomain的缓慢是它不得不重新启动wcf使用的所有System。* dll。第一个应用程序域可以使用这些dll的早期本地版本,因此它没有相同的启动成本。

调查LoaderOptimizationAttribute那斯托建议,确实似乎解决这个问题,即使用MultiDomain or MultiDomainHost导致第二AppDomain中采取相同的时间量首次超过WCF访问的东西

这里后,你可以看到默认选项,注意如何在第二的AppDomain没有组件的说母语,这意味着他们都不得不rejitted,这是正在采取所有的时间

enter image description here

以下是添加了后LoaderOptimiza (LoaderOptimization.MultiDomain)到Main。你可以看到,一切都加载到共享的AppDomain

enter image description here

下面是用户LoaderOptimization(LoaderOptimization.MultiDomainHost)主后。你可以看到,所有的系统DLL是共享的,但我自己的DLL和任何未在海关总署2单独加载到每个AppDomain中

enter image description here

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

回答

9

您可以用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. 
+1

谢谢,补充说,确实可以将新AppDomain中的时间缩短为第一个35ms和第二个2ms第二,但这会防止卸载任何程序集加载到第二个AppDomain?应用程序使用AppDomains的一个原因是因为它是一个长期运行的服务,需要加载自定义代码来执行一个操作,所以我仍然需要能够卸载一些程序集,以便该服务的大小不会增加无界限。 – BrandonAGr 2012-04-19 16:24:54

+1

此设置不应阻止从第二个AppDomain卸载程序集。 – 2012-04-19 16:43:12

+0

哪个应用正在制作屏幕截图? Sysinternals的东西? – 2012-04-21 00:10:20

1

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

否则我会猜测这是加载所有dll所需的时间。尝试将代理创建从对服务的实际调用中分离出来,以查看需要花费的时间。

+0

我以前检查过代理设置,我不认为就是这样。我也怀疑它的加载DLL,因为如果它是那么为什么不第一个WCF尝试看到从新的AppDomain相同的延迟?我注意到的一件事是,当它创建新的AppDomain时,CPU使用率在2.5秒内达到最大值 – BrandonAGr 2012-04-15 00:17:44

1

我发现了following article,它讲述了只有第一个AppDomain可以如何使用本机映像dll,因此小孩appdomain总是会被强制执行很多初始AppDomain不需要的东西。这可能会导致我所看到的性能影响,但是有可能不会得到这种性能损失?

如果存在程序集的本机映像,则只有第一个AppDomain 可以使用本机映像。所有其他应用程序域将不得不 JIT编译代码,这可能导致显着的CPU成本。