首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >在新的AppDomain中建立的第一个WCF连接非常慢

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

Stack Overflow用户
提问于 2012-04-14 07:37:46
回答 3查看 2.8K关注 0票数 18

我有一个我使用的库,它使用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毫秒,调用接口上的方法需要1098毫秒。

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

编辑

在使用perfmon和.NET CLR加载对象之后,我可以看到当它加载第二个AppDomain时,它加载到内存中的类比它最初加载的类要多得多。第一行是我在第一个appdomain之后的暂停,它加载了218个类。第二个AppDomain导致总共装入1,944个类。

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

更新

答案被证明是因为只有一个AppDomain能够利用本机映像系统dlls的事实。因此,在第二个应用程序域中的缓慢是它不得不重新编译所有的系统。* wcf使用的all。第一个appdomain可以使用这些dll的原生版本,因此它没有相同的启动成本。

在研究了Petar建议的LoaderOptimizationAttribute之后,这似乎确实解决了这个问题,在第二个AppDomain中使用任何一个结果都需要与第一次通过wcf访问内容相同的时间。

在这里,您可以看到默认选项,请注意在第二个AppDomain中,没有一个程序集显示为本机,这意味着它们都必须重新编译,这就是所有时间所花费的时间

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

下面是用户LoaderOptimization(LoaderOptimization.MultiDomainHost)到main之后的代码。您可以看到,所有系统AppDomain都是共享的,但我自己的dll和任何不在GAC中的dll都分别加载到每个dll中。

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

EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/10149651

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档