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

时间:2012-04-13 23:37:46

标签: c# wcf performance c#-4.0 appdomain

我使用的库使用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<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(string,!! 0,!! 0,!! 0)2,542.66 Tps.Core.Settings.Retriever.TryGetSetting(string,!! 0&amp;)2,522.03 Tps.Core.Settings.ServiceModel.WcfHelper.GetClient()1,371.21 Tps.Core.Settings.ServiceModel.IClientChannelExtensions.CallWithRetry(类System.ServiceModel.IClientChannel)1,098.83

修改

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

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

enter image description here

更新

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

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

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

enter image description here

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

enter image description here

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

enter image description here

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

3 个答案:

答案 0 :(得分: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 :(得分:1)

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

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

答案 2 :(得分:1)

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

  

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