我有一个使用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个总类。
我认为它的所有这些类正在加载所有的时间,所以现在的问题是,它加载了什么类,为什么?
UPDATE
答案原来是因为事实上只有一个AppDomain中能够采取机映像系统DLL的优势。所以第二个appdomain的缓慢是它不得不重新启动wcf使用的所有System。* dll。第一个应用程序域可以使用这些dll的早期本地版本,因此它没有相同的启动成本。
调查LoaderOptimizationAttribute那斯托建议,确实似乎解决这个问题,即使用MultiDomain or MultiDomainHost导致第二AppDomain中采取相同的时间量首次超过WCF访问的东西
这里后,你可以看到默认选项,注意如何在第二的AppDomain没有组件的说母语,这意味着他们都不得不rejitted,这是正在采取所有的时间
以下是添加了后LoaderOptimiza (LoaderOptimization.MultiDomain)到Main。你可以看到,一切都加载到共享的AppDomain
下面是用户LoaderOptimization(LoaderOptimization.MultiDomainHost)主后。你可以看到,所有的系统DLL是共享的,但我自己的DLL和任何未在海关总署2单独加载到每个AppDomain中
所以对于使用MultiDomainHost就是答案提示这个问题的服务,因为它具有快速启动时间,我可以卸载AppDomains以删除服务使用的动态构建的程序集
谢谢,补充说,确实可以将新AppDomain中的时间缩短为第一个35ms和第二个2ms第二,但这会防止卸载任何程序集加载到第二个AppDomain?应用程序使用AppDomains的一个原因是因为它是一个长期运行的服务,需要加载自定义代码来执行一个操作,所以我仍然需要能够卸载一些程序集,以便该服务的大小不会增加无界限。 – BrandonAGr 2012-04-19 16:24:54
此设置不应阻止从第二个AppDomain卸载程序集。 – 2012-04-19 16:43:12
哪个应用正在制作屏幕截图? Sysinternals的东西? – 2012-04-21 00:10:20