简体   繁体   中英

Slow WCF Host when hosted in another AppDomain

I am having a weird problem. If the WCF host is hosted in the original AppDomain, the code takes 1.25s to execute. However if I put it in a new AppDomain, despite the fact that I still use net.pipe or net.tcp to talk to it, the process takes 4.7s to run. Here is the code with app.config in the commented out block on the bottom.

namespace ConsoleApplication2
{
    using System;
    using System.Collections.Concurrent;
    using System.Diagnostics;
    using System.Linq;
    using System.Net;
    using System.Net.NetworkInformation;
    using System.Reflection;
    using System.ServiceModel;
    using System.Threading.Tasks;

    using Client;

    internal class Program
    {
        #region Methods

        private static void Main(string[] args)
        {
            var p = new Program();
            p.Execute();
        }

        #endregion

        public void Execute()
        {
            var hosts = Enumerable.Range(0, 1).Select(CreateNewHost).ToArray();
            var r = new Random();
            var cb = new ConcurrentDictionary<int, string>();
            Action a = () =>
            {
                try
                {
                    Parallel.For(
                        0,
                        10000,
                        i =>
                        {
                            //string ep = String.Format("net.pipe://localhost/iService{0}", hosts[r.Next(0, hosts.Length)]);
                            string ep = String.Format("net.tcp://localhost:{0}/iService", hosts[r.Next(0, hosts.Length)]);
                            string s=null;
                            //using (var cli = new ServiceClassClient("NetNamedPipeBinding_IServiceClass", ep))
                            using (var cli = new ServiceClassClient("NetTcpBinding_IServiceClass", ep))
                            {
                                s = cli.Ping();
                            }
                            if (!String.IsNullOrEmpty(s))
                            {
                                cb[i] = s;
                            }
                        });
                }
                catch (AggregateException aggregateException)
                {
                    Console.WriteLine(aggregateException);
                }
            };
            Console.WriteLine("\n\nIt took {0:G}", a.TimeThis());
            Console.ReadKey();
        }

        static int CreateNewHost(int s)
        {
            //uncomment for in-process host
            //var h1 = new Host();
            //return h1.Port;

            var appDomain = AppDomain.CreateDomain(
                "A" + s,
                null,
                new AppDomainSetup
                    {
                        LoaderOptimization = LoaderOptimization.MultiDomain,
                        DisallowBindingRedirects = true
                    });
            var assemblyName = Assembly.GetAssembly(typeof(Host)).FullName;
            var h = appDomain.CreateInstanceAndUnwrap(assemblyName, typeof(Host).FullName) as Host;
            return h.Port;
        }
    }

    //comment out MarshalByRefObject for in-process host
    public class Host:MarshalByRefObject
    {
        #region Fields

        private readonly ServiceHost host;

        private readonly int port;

        #endregion

        #region Constructors and Destructors

        public Host()
        {
            this.port = this.GetFreePort();
            var ub = new UriBuilder { Host = "localhost", Port = this.port, Scheme = "net.tcp" };
            var up = new UriBuilder { Host = "localhost", Scheme = "net.pipe", Path = "iService" + this.port };
            this.host = new ServiceHost(typeof(ServiceClass), ub.Uri);
            var netNamedPipeBinding = new NetNamedPipeBinding(NetNamedPipeSecurityMode.None);
            this.host.AddServiceEndpoint(typeof(IServiceClass), netNamedPipeBinding, up.Uri);
            var un = new UriBuilder { Host = "localhost", Port = this.port, Scheme = "net.tcp", Path = "iService" };
            var netTcpBinding = new NetTcpBinding(SecurityMode.None);
            this.host.AddServiceEndpoint(typeof(IServiceClass), netTcpBinding, un.Uri);

//#if DEBUG
            //var us = new UriBuilder { Host = "localhost", Port = this.port, Scheme = "http", Path = "iServiceMeta" };
            //var smb = new ServiceMetadataBehavior { HttpGetEnabled = true, HttpGetUrl = us.Uri };
            //this.host.Description.Behaviors.Add(smb);
//#endif
            this.host.Open();
            Console.WriteLine("Listening at {0}", this.host.BaseAddresses[0].AbsoluteUri);
        }

        #endregion

        #region Public Properties

        public int Port
        {
            get
            {
                return this.port;
            }
        }

        public ServiceHost ServiceHost
        {
            get
            {
                return this.host;
            }
        }

        #endregion

        #region Methods

        private int GetFreePort()
        {
            TcpConnectionInformation[] connections = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections();
            IPEndPoint[] listeners = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();
            var udps = IPGlobalProperties.GetIPGlobalProperties().GetActiveUdpListeners();
            var r = new Random();
            int port;
            do
            {
                port = r.Next(1025, 65534);
            }
            while (listeners.Any(a => a.Port == port) || connections.Any(a => a.LocalEndPoint.Port == port) || udps.Any(a=>a.Port==port));
            return port;
        }

        #endregion
    }

    [ServiceContract]
    internal interface IServiceClass
    {
        #region Public Methods and Operators

        [OperationContract]
        string Ping();

        #endregion
    }

    internal class ServiceClass : IServiceClass
    {
        #region Public Methods and Operators

        public string Ping()
        {
            return ((new Random()).NextDouble() * (new Random()).NextDouble()).ToString("F11");
        }

        #endregion
    }

    public static class Extensions
    {
        #region Public Methods and Operators

        public static TimeSpan TimeThis(this Action action)
        {
            var sw = new Stopwatch();
            sw.Start();
            action.Invoke();
            sw.Stop();
            return sw.Elapsed;
        }

        #endregion
    }
}

//------------------------------------------------------------------------------
// <auto-generated>
//     This code was generated by a tool.
//     Runtime Version:4.0.30319.18033
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------

namespace Client
{

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    [System.ServiceModel.ServiceContractAttribute(ConfigurationName = "IServiceClass")]
    public interface IServiceClass
    {

        [System.ServiceModel.OperationContractAttribute(Action = "http://tempuri.org/IServiceClass/Ping", ReplyAction = "http://tempuri.org/IServiceClass/PingResponse")]
        string Ping();
    }

    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    public interface IServiceClassChannel : IServiceClass, System.ServiceModel.IClientChannel
    {
    }

    [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("System.ServiceModel", "4.0.0.0")]
    public partial class ServiceClassClient : System.ServiceModel.ClientBase<IServiceClass>, IServiceClass
    {

        public ServiceClassClient()
        {
        }

        public ServiceClassClient(string endpointConfigurationName)
            : base(endpointConfigurationName)
        {
        }

        public ServiceClassClient(string endpointConfigurationName, string remoteAddress)
            : base(endpointConfigurationName, remoteAddress)
        {
        }

        public ServiceClassClient(string endpointConfigurationName, System.ServiceModel.EndpointAddress remoteAddress)
            : base(endpointConfigurationName, remoteAddress)
        {
        }

        public ServiceClassClient(System.ServiceModel.Channels.Binding binding, System.ServiceModel.EndpointAddress remoteAddress)
            : base(binding, remoteAddress)
        {
        }

        public string Ping()
        {
            return base.Channel.Ping();
        }
    }
}


/*
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <bindings>
      <netNamedPipeBinding>
        <binding name="NetNamedPipeBinding_IServiceClass">
          <security mode="None" />
        </binding>
      </netNamedPipeBinding>
      <netTcpBinding>
        <binding name="NetTcpBinding_IServiceClass">
          <security mode="None" />
        </binding>
      </netTcpBinding>
    </bindings>
    <client>
      <endpoint address="" binding="netNamedPipeBinding"
          bindingConfiguration="NetNamedPipeBinding_IServiceClass" contract="IServiceClass"
          name="NetNamedPipeBinding_IServiceClass" />
      <endpoint address="" binding="netTcpBinding"
          bindingConfiguration="NetTcpBinding_IServiceClass" contract="IServiceClass"
          name="NetTcpBinding_IServiceClass" />
    </client>
  </system.serviceModel>
</configuration>
*/

You might get improved performance by decorating your Main method with LoaderOptimization attribute. This shares common resources across app domains.

[LoaderOptimization(LoaderOptimization.MultiDomain)]

See this answer for more details.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM