Tengo una biblioteca que uso que usa WCF para llamar a un servicio http para obtener configuraciones. Normalmente, la primera llamada tarda unos 100 milisegundos y las llamadas siguientes tardan solo unos pocos milisegundos. Pero descubrí que cuando creo un nuevo AppDomain, la primera llamada WCF de ese AppDomain toma más de 2,5 segundos.La primera conexión WCF realizada en AppDomain nuevo es muy lenta
¿Alguien tiene una explicación o una corrección de por qué la primera creación de un canal WCF en un nuevo dominio de aplicación llevaría tanto tiempo?
Estos son los resultados de referencia (Cuando se ejecuta sin depurador asociado en la liberación en 64 bits), cuenta de cómo en el segundo conjunto de números de las primeras conexiones se hace cargo de 25x ya
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
Esto no es un ejemplo completo pero muestra más de cómo produje estos números:
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);
}
}
La llamada a AppServSettings.ServiceBaseUrlBatch es la creación de un canal a un servicio y llamar a un solo método. He usado wireshark para ver la llamada y solo lleva milisegundos obtener una respuesta del servicio. Se crea el canal con el siguiente código:
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();
}
partir de perfiles de la aplicación se muestra que en el primer caso la construcción de la fábrica de canal y la creación del canal y llamando al método tarda menos de 100 milisegundos
En el El nuevo AppDomain que construye la fábrica de canales tomó 763 milisegundos, 521 milisegundos para crear el canal, 1,098 milisegundos para llamar al método en la interfaz.
TestSettingsRepoInAppDomain.DomainRunner.Run() 2,660.00 TestSettingsRepoInAppDomain.AppServSettings.get_ServiceBaseUrlBatch() 2,543.47 Tps.Core.Settings.Retriever.GetSetting (cadena, !! 0, 0 !!, !! 0) 2,542.66 TPS. Core.Settings.Retriever.TryGetSetting (cadena, !! & 0) 2,522.03 Tps.Core.Settings.ServiceModel.WcfHelper.GetClient() 1,371.21 Tps.Core.Settings.ServiceModel.IClientChannelExtensions.CallWithRetry (System.ServiceModel.IClientChannel clase) 1,098.83
EDITAR
Después de usar perfmon con el objeto .NET CLR Cargando, puedo ver que cuando carga el segundo dominio de aplicación, está cargando más clases en la memoria que inicialmente. La primera línea plana es una pausa que puse después del primer appdomain, allí tiene 218 clases cargadas. El segundo dominio de aplicación causa que se carguen 1.944 clases en total.
Supongo que es la carga de todas estas clases que está tomando todo el tiempo, por lo que ahora la pregunta es, ¿qué clases está cargando y por qué?
ACTUALIZACIÓN
La respuesta resulta ser debido al hecho de que sólo un dominio de aplicación es capaz de tomar ventaja de las DLL del sistema de imágenes nativas. Entonces, la lentitud en el segundo dominio de aplicación fue tener que volver a configurar todos los System. * Dlls utilizados por wcf. El primer dominio de aplicación podría usar las versiones nativas de esos dlls, por lo que no tenía el mismo costo de inicio.
Después de investigar la LoaderOptimizationAttribute que Petar sugirió, que, efectivamente, parecía solucionar el problema, ya sea utilizando MultiDomain or MultiDomainHost resultados en el segundo dominio de aplicación para tomar la misma cantidad de tiempo que la primera vez que accede cosas una WCF
Aquí puede ver la opción por defecto, tenga en cuenta cómo en el segundo dominio de aplicación ninguno de los conjuntos dicen nativo, lo que significa que todos tenían que ser rejitted, que es lo que estaba teniendo todo el tiempo
Aquí es después de añadir el LoaderOptimiza ción (LoaderOptimization.MultiDomain) a Main. Se puede ver que todo lo que se carga en el dominio de aplicación compartida
Aquí es después LoaderOptimization usuario (LoaderOptimization.MultiDomainHost) al principal. Se puede ver que todas las DLL del sistema son compartidos, pero mis propios archivos DLL y cualquier no en el GAC se cargan por separado en cada dominio de aplicación
Así que por el servicio que motivó esta pregunta utilizando MultiDomainHost es la respuesta, porque tiene un tiempo de inicio rápido y puedo descargar AppDomains para eliminar los ensamblados construidos dinámicamente que usa el servicio
Gracias, agregando que de hecho reduce el tiempo en el nuevo Dominio de aplicación de 35 ms para el primero y 2 ms para el segundo, pero, ¿evitaría esto que se descarguen los ensamblajes cargados en el segundo dominio de la aplicación? Uno de los motivos por los que la aplicación utiliza AppDomains es porque es un servicio de ejecución prolongada que necesita cargar código personalizado para realizar una acción, por lo que aún tendría que poder descargar algunos ensamblajes para que el tamaño del servicio no crezca sin límites. . – BrandonAGr
Esta configuración no debería evitar la descarga de ensamblados desde el segundo dominio de la aplicación. –
¿Qué aplicación está produciendo las capturas de pantalla? Sysinternals cosas? –