c# - Fabricante de Slow SoapHttpClientProtocol
.net performance (6)
Es posible que desee examinar la herramienta Sgen.exe
que viene con .NET. También hay algo útil en la página "Build" de las propiedades del proyecto C # de Visual Studio, en la parte inferior, llamada "Build serialization assembly" (ensamblaje de serialización de compilación) que ejecuta automáticamente Sgen
por usted.
Estoy haciendo algunos experimentos con Microsoft Dynamics CRM. Usted interactúa con él a través de servicios web y he agregado una referencia web a mi proyecto. La interfaz del servicio web es muy rica, y el "Reference.cs" generado es de aproximadamente 90k loc.
Estoy usando la referencia web en una aplicación de consola. A menudo cambio algo, recompilo y corro. La compilación es rápida, pero la actualización de la referencia del servicio web es muy lenta, tarda unos 15-20 segundos: CrmService service = new CrmService();
La creación de perfiles revela que todo el tiempo se usa en el constructor SoapHttpClientProtocol.
El culpable es aparentemente el hecho de que el código de serialización XML (no incluido en el 90c loc mencionado anteriormente) se genera en tiempo de ejecución, antes de ser JIT. Esto sucede durante la llamada del constructor. La espera es bastante frustrante cuando se juega e intenta cosas.
He intentado varias combinaciones de sgen.exe, ngen y XGenPlus (que lleva varias horas y genera 500 MB de código adicional) pero fue en vano. Consideré implementar un servicio de Windows que tenga pocas instancias de CrmService listas para distribuirse cuando sea necesario, pero eso parece excesivo.
¿Algunas ideas?
Creo que este no es un problema de SGEN. Miré el código del constructor y veo que está reflejando mucho (basado en XmlIncludeAttribute en la clase). Se refleja en todos ellos, y puede llevar mucho tiempo.
Hay un ensamblaje XmlSerializer pregenerado que viene con CRM. Compruebe si tiene SdkTypeProxy.XmlSerializers.dll y SdkProxy.XmlSerializers.dll en el GAC.
Si no lo hace, entonces eso significa que cuando crea CrmService, .net generará el ensamblado XmlSerializer, lo que puede llevar algo de tiempo. Espero que esto ayude
Lo siguiente se extrajo de este hilo en los foros de VMWare:
Hola amigos,
Hemos encontrado que sgen.exe funciona. Es solo que hay un par de pasos adicionales más allá de la pregeneración del dll del serializador que perdimos en este hilo. Aquí está la instrucción detallada
PROBLEMA
Al usar el VIM 2.0 SDK de .NET, se necesita mucho tiempo para crear una instancia de la clase VimService. (La clase VimService es la clase de proxy generada ejecutando ''wsdl.exe vim.wsdl vimService.wsdl'')
En otras palabras, la siguiente línea de código:
_service = new VimService();
Podría tomar unos 50 segundos para ejecutar.
PORQUE
Aparentemente, .NET XmlSerializer
usa los atributos System.Xml.Serialization.*
anotar las clases de proxy para generar el código de serialización en tiempo de ejecución. Cuando las clases proxy son muchas y grandes, como es el código en VimService.cs, la generación del código de serialización puede llevar mucho tiempo.
SOLUCIÓN
Este es un problema conocido sobre cómo funciona el serializador Microsoft .NET.
Aquí hay algunas referencias que proporciona MSDN sobre cómo resolver este problema:
http://msdn2.microsoft.com/en-us/library/bk3w6240.aspx http://msdn2.microsoft.com/en-us/library/system.xml.serialization.xmlserializerassemblyattribute.aspx
Desafortunadamente, ninguna de las referencias anteriores describe la solución completa al problema. En su lugar, se centran en cómo pregenerar el código de serialización XML.
La solución completa implica los siguientes pasos:
Crear un ensamblado (una DLL) con el código de serializador XML generado previamente
Elimine todas las referencias a los atributos System.Xml.Serialization. * Del código proxy (es decir, desde el archivo VimService.cs)
Anote la clase de proxy principal con XmlSerializerAssemblyAttribute para apuntar al lugar donde se encuentra el ensamblado de serializador XML.
Saltarse el paso 2 conduce a una mejora de solo el 20% en el tiempo de instanciación para la clase VimService
. Omitir el paso 1 o 3 conduce a un código incorrecto. Con los tres pasos, se logra una mejora del 98%.
Aquí hay instrucciones paso a paso:
Antes de comenzar, asegúrese de estar utilizando las herramientas .NET verison 2.0. Esta solución no funcionará con la versión 1.1 de .NET porque la herramienta sgen y XmlSerializationAssemblyAttribute
solo están disponibles en la versión 2.0 de .NET
Genere el archivo VimService.cs del WSDL, usando wsdl.exe:
wsdl.exe vim.wsdl vimService.wsdl
Esto generará el archivo VimService.cs en el directorio actual
Compila VimService.cs en una biblioteca
csc /t:library /out:VimService.dll VimService.cs
Use la herramienta sgen para pregenerar y compilar los serializadores XML:
sgen /p VimService.dll
Esto dará como resultado el VimService.XmlSerializers.dll en el directorio actual
Vuelva al archivo VimService.cs y elimine todos los atributos
System.Xml.Serialization.*
. Debido a que el código de código es grande, la mejor manera de lograrlo es mediante el uso de alguna herramienta de sustitución de expresiones regulares. Tenga cuidado al hacer esto porque no todos los atributos aparecen en una línea por sí mismos. Algunos están alineados como parte de una declaración de método.Si encuentra este paso difícil, aquí hay una forma simplificada de hacerlo:
Suponiendo que está escribiendo C #, realice una sustitución global en la siguiente cadena:
[System.Xml.Serialization.XmlIncludeAttribute
y reemplázalo con:
// [System.Xml.Serialization.XmlIncludeAttribute
Esto eliminará los atributos
Xml.Serialization
que son los principales culpables de la ralentización al comentarlos. Si está utilizando otro lenguaje .NET, simplemente modifique la cadena reemplazada para que sea prefijo, comentado de acuerdo con la sintaxis de ese idioma. Este enfoque simplificado le dará la mayor parte de la aceleración que puede obtener. Eliminar el resto de los atributos Xml.Serialization solo logra una aceleración extra de 0.2 segundos.Agregue el siguiente atributo a la clase VimService en VimService.cs:
[System.Xml.Serialization.XmlSerializerAssemblyAttribute(AssemblyName = "VimService.XmlSerializers")]
Deberías terminar con algo como esto:
// ... Some code here ... [System.Xml.Serialization.XmlSerializerAssemblyAttribute(AssemblyName = "VimService.XmlSerializers")] public partial class VimService : System.Web.Services.Protocols.SoapHttpClientProtocol { // ... More code here
Regenerar la biblioteca VimSerice.dll por
csc /t:library /out:VimService.dll VimService.cs
Ahora, desde su aplicación, puede agregar una referencia a la biblioteca VimSerice.dll.
Ejecute su aplicación y verifique que se reduzca el tiempo de instanciación del objeto VimService.
NOTAS ADICIONALES
La herramienta sgen es un poco de una caja negra y su comportamiento varía según lo que tenga en su archivo Machine.config. Por ejemplo, de forma predeterminada se supone que debe salir del código optimizado que no es de depuración, pero ese no es siempre el caso. Para obtener algo de visibilidad de la herramienta, use el indicador / k en el paso 3, que hará que conserve todos sus archivos generados temporalmente, incluidos los archivos de origen y los archivos de opciones de línea de comando que generó.
Incluso después de corregir lo anterior, el tiempo que se tarda en crear una instancia de la clase VimService por primera vez no es instantáneo (1,5 segundos). Según la observación empírica, parece que la mayoría del tiempo restante se debe al procesamiento de los atributos SoapDocumentMethodAttribute
. En este momento, no está claro cómo se puede reducir este tiempo. El ensamblado XmlSerializer pregenerado no tiene en cuenta los atributos relacionados con SOAP, por lo que estos atributos deben permanecer en el código. La buena noticia es que solo la primera instanciación de la clase VimService para esa aplicación lleva mucho tiempo. Por lo tanto, si los 1.5 segundos extra son un problema, se podría tratar de crear una instancia ficticia de esta clase al comienzo de la aplicación como un medio para mejorar la experiencia del usuario en el inicio de sesión.
Encontré este hilo cuando trataba de averiguar por qué SoapHttpClientProtocol
mis llamadas iniciales SoapHttpClientProtocol
.
Descubrí que al configurar el Proxy en nulo / vacío se detuvo el Proxy AutoDetect. Esto tomó hasta 7 segundos en la llamada inicial:
this.Proxy = GlobalProxySelection.GetEmptyWebProxy();
He utilizado la respuesta anterior como guía y avancé unos pasos hacia adelante, creando un script para automatizar el proceso. El script está hecho de dos archivos:
generateproxy.bat:
REM if your path for wsdl, csc or sgen is missing, please add it here (it varies from machine to machine)
set PATH=%PATH%;C:/Program Files (x86)/Microsoft SDKs/Windows/v10.0A/bin/NETFX 4.6.1 Tools;C:/Program Files (x86)/MSBuild/14.0/Bin
wsdl http://localhost:57237/VIM_WS.asmx?wsdl REM create source code out of WSDL
PowerShell.exe -ExecutionPolicy Bypass -Command "& ''%~dpn0.ps1''" REM proces source code (remove annotations, add other annotation, put class into namespace)
csc /t:library /out:references/VIM_Service.dll VIM_WS.cs REM compile source into dll
sgen /p references/VIM_Service.dll /force REM generate serializtion dll
generateproxy.ps1
(Get-Content VIM.cs) |
ForEach-Object {
$_ -replace "(?<attr>/[global::System.Xml.Serialization.[^/]]*/])", "/*${attr}*/" `
-replace "public partial class VIM", "[System.Xml.Serialization.XmlSerializerAssemblyAttribute(AssemblyName = ""VIM_Service.XmlSerializers"")] `npublic partial class VIM" `
-replace "using System;", "namespace Classes.WS_VIM { `n`nusing System;"
} |
Set-Content VIM.cs
Add-Content VIM.cs "`n}"
He agregado esos dos archivos al proyecto del cliente, y en el evento de preconstrucción he agregado líneas
cd../..
generateproxy
Por lo tanto, antes de cada compilación, las clases proxy se regeneran, y el desarrollador tiene (casi) sin necesidad de pensar en ello. Al compilar, WS debe estar en funcionamiento y su URL debe estar en el archivo bat. Como resultado de prebuild, dos archivos dll se regenerarán en las referencias de la subcarpeta del proyecto del cliente. Después de la primera ejecución de scripts, debe agregar una referencia a la nueva dll.