Optimización del rendimiento de las canalizaciones
En este tema se describen las directrices para optimizar el rendimiento de las canalizaciones en una solución de BizTalk Server.
Procedimientos recomendados para optimizar el rendimiento de las canalizaciones de BizTalk Server
Dado que los componentes de canalización tienen un impacto significativo en el rendimiento (por ejemplo, un componente de canalización de paso a través realiza hasta un 30 % mejor que un componente de canalización ensamblador o desensamblador XML), asegúrese de que los componentes de canalización personalizados funcionan de forma óptima antes de implementarlos en la implementación. Minimice el número de componentes de canalización en las canalizaciones personalizadas si desea maximizar el rendimiento general de la aplicación de BizTalk.
También puede mejorar el rendimiento general reduciendo la frecuencia de persistencia de mensajes en el componente de canalización y codificando el componente para minimizar la redundancia. Todos los ensamblados personalizados y en particular los artefactos que podrían interrumpir el rendimiento, como los componentes de seguimiento personalizados, deben probarse por separado en condiciones de carga pesada para observar su comportamiento cuando el sistema funciona a plena capacidad y para encontrar posibles cuellos de botella.
Si necesita leer el mensaje entrante dentro de un componente de canalización, evite cargar todo el documento en la memoria mediante un objeto XmlDocument . La cantidad de espacio que requiere una instancia de la clase XmlDocument para cargar y crear una representación en memoria de un documento XML es de hasta 10 veces el tamaño real del mensaje. Para leer un mensaje, debe usar un objeto XmlTextReader junto con una instancia de las siguientes clases:
VirtualStream (Microsoft.BizTalk.Streaming.dll): el código fuente de esta clase se encuentra en dos ubicaciones en el SDK de Pipelines de la siguiente manera: SDK\Samples\Pipelines\ArbitraryXPathPropertyHandler y SDK\Samples\Pipelines\SchemaResolverComponent\SchemaResolverFlatFileDasm.
ReadOnlySeekableStream (Microsoft.BizTalk.Streaming.dll).
SeekAbleReadOnlyStream : el código fuente de esta clase se encuentra en dos ubicaciones en el SDK de Pipelines de la siguiente manera: SDK\Samples\Pipelines\ArbitraryXPathPropertyHandler y SDK\Samples\Pipelines\SchemaResolverComponent\SchemaResolverFlatFileDasm.
Use las canalizaciones estándar PassThruReceive y PassThruTransmit siempre que sea posible. No contienen ningún componente de canalización y no realizan ningún procesamiento del mensaje. Por este motivo, garantizan el máximo rendimiento en la recepción o envío de mensajes. Puede usar una canalización PassThruReceive en una ubicación de recepción si necesita publicar un documento binario en el Cuadro de mensajes de BizTalk y una canalización PassThruTransmit en un puerto de envío si necesita enviar un mensaje binario. También puede usar la canalización PassThruTransmit en un puerto de envío físico enlazado a una orquestación si el mensaje se ha formateado y está listo para transmitirse. Tendrá que usar un enfoque diferente si necesita realizar una de las siguientes acciones:
Promover propiedades en el contexto de un mensaje XML o archivo plano de entrada.
Aplicar un mapa dentro de una ubicación de recepción.
Aplicar un mapa en una orquestación que se suscribe a un mensaje.
Aplicar un mapa en un puerto de envío que se suscribe a un mensaje.
Para realizar una de estas acciones, debe sondear y detectar el tipo de documento dentro de la canalización de recepción y asignar el valor (espacio de nombres#nombre-raíz) a la propiedad de contexto MessageType. Normalmente, esta operación se realiza mediante un componente de desensamblador, como el componente Desensamblador xml (XmlDasmComp) o el componente de desensamblador de archivos planos (FFDasmComp). En este caso, debe usar un estándar (por ejemplo, una canalización XmlReceive) o una canalización personalizada que contenga un componente estándar o de desensamblador personalizado.
Adquiera los recursos lo más tarde posible y los libere lo antes posible. Por ejemplo, si necesita acceder a los datos de una base de datos, abra la conexión lo antes posible y ciérrela lo antes posible. Use la instrucción using de C# para liberar implícitamente objetos descartables o el bloque finally de una instrucción try-catch-finally para eliminar explícitamente los objetos. Instrumente el código fuente para que los componentes sean sencillos de depurar.
Elimine los componentes de las canalizaciones que no sean estrictamente necesarios para acelerar el procesamiento de mensajes.
Dentro de una canalización de recepción, debe promover elementos al contexto del mensaje solo si los necesita para el enrutamiento de mensajes (orquestaciones, puertos de envío) o degradación de las propiedades de contexto de mensaje (puertos de envío).
Si necesita incluir metadatos con un mensaje y no usa los metadatos con fines de enrutamiento o degradación, use el método IBaseMessageContext.Write en lugar del método IBaseMessageContext.Promote .
Si necesita extraer información de un mensaje mediante una expresión XPath, evite cargar todo el documento en la memoria mediante un objeto XmlDocument solo para usar los métodos SelectNodes o SelectSingleNode . Como alternativa, use las técnicas descritas en Optimización del uso de memoria con streaming.
Uso del streaming para minimizar la superficie de memoria necesaria al cargar mensajes en canalizaciones
En las técnicas siguientes se describe cómo minimizar la superficie de memoria de un mensaje al cargar el mensaje en una canalización.
Uso de ReadOnlySeekableStream y VirtualStream para procesar un mensaje desde un componente de canalización
Se considera un procedimiento recomendado para evitar cargar todo el mensaje en la memoria dentro de los componentes de la canalización. Un enfoque preferible es encapsular el flujo entrante con una implementación de flujo personalizada y, a continuación, a medida que se realizan solicitudes de lectura, la implementación de secuencia personalizada lee la secuencia subyacente, ajustada y procesa los datos tal y como se lee (de una manera pura de streaming). Esto puede ser muy difícil de implementar y puede que no sea posible, dependiendo de lo que se debe hacer con la secuencia. En este caso, use las clases ReadOnlySeekableStream y VirtualStream expuestas por el Microsoft.BizTalk.Streaming.dll. También se proporciona una implementación de estas en El controlador de propiedades XPath arbitrario (BizTalk Server ejemplo) (https://go.microsoft.com/fwlink/?LinkId=160069) en el SDK de BizTalk.ReadOnlySeekableStream garantiza que el cursor se puede cambiar de posición al principio de la secuencia. VirtualStream usará un objeto MemoryStream internamente, a menos que el tamaño supere un umbral especificado, en cuyo caso escribirá la secuencia en el sistema de archivos. El uso de estos dos flujos en combinación (mediante VirtualStream como almacenamiento persistente para ReadOnlySeekableStream) proporciona funcionalidades de "búsqueda" y "desbordamiento al sistema de archivos". Esto admite el procesamiento de mensajes de gran tamaño sin cargar todo el mensaje en la memoria. El código siguiente se puede usar en un componente de canalización para implementar esta funcionalidad.
int bufferSize = 0x280;
int thresholdSize = 0x100000;
Stream vStream = new VirtualStream(bufferSize, thresholdSize);
Stream seekStream = new ReadOnlySeekableStream(inboundStream, vStream, bufferSize);
Este código proporciona un "umbral de desbordamiento" al exponer las variables bufferSize y thresholdSize en cada ubicación de recepción o configuración del puerto de envío. A continuación, los desarrolladores o administradores pueden ajustar este umbral de desbordamiento para distintos tipos de mensajes y configuraciones diferentes (como 32 bits frente a 64 bits).
Usar XPathReader y XPathCollection para extraer un objeto IBaseMessage determinado desde dentro de un componente de canalización personalizado.
Si es necesario extraer valores específicos de un documento XML, en lugar de usar los métodos SelectNodes y SelectSingleNode expuestos por la clase XmlDocument, use una instancia de la clase XPathReader proporcionada por el ensamblado Microsoft.BizTalk.XPathReader.dll como se muestra en el ejemplo de código siguiente.
Nota
En el caso de mensajes más pequeños especialmente, el uso de xmlDocument con SelectNodes o SelectSingleNode puede proporcionar un mejor rendimiento que el uso de XPathReader, pero XPathReader le permite mantener un perfil de memoria plana para la aplicación.
En este ejemplo se muestra cómo usar XPathReader y XPathCollection para extraer un objeto IBaseMessage determinado desde un componente de canalización personalizado.
public IBaseMessage Execute(IPipelineContext context, IBaseMessage message)
{
try
{
...
IBaseMessageContext messageContext = message.Context;
if (string.IsNullOrEmpty(xPath) && string.IsNullOrEmpty(propertyValue))
{
throw new ArgumentException(...);
}
IBaseMessagePart bodyPart = message.BodyPart;
Stream inboundStream = bodyPart.GetOriginalDataStream();
VirtualStream virtualStream = new VirtualStream(bufferSize, thresholdSize);
ReadOnlySeekableStream readOnlySeekableStream = new ReadOnlySeekableStream(inboundStream, virtualStream, bufferSize);
XmlTextReader xmlTextReader = new XmlTextReader(readOnlySeekableStream);
XPathCollection xPathCollection = new XPathCollection();
XPathReader xPathReader = new XPathReader(xmlTextReader, xPathCollection);
xPathCollection.Add(xPath);
bool ok = false;
while (xPathReader.ReadUntilMatch())
{
if (xPathReader.Match(0) && !ok)
{
propertyValue = xPathReader.ReadString();
messageContext.Promote(propertyName, propertyNamespace, propertyValue);
ok = true;
}
}
readOnlySeekableStream.Position = 0;
bodyPart.Data = readOnlySeekableStream;
}
catch (Exception ex)
{
if (message != null)
{
message.SetErrorInfo(ex);
}
...
throw ex;
}
return message;
}
Uso de XMLReader y XMLWriter con XMLTranslatorStream para procesar un mensaje desde un componente de canalización
Otro método para implementar un componente de canalización personalizado que usa un enfoque de streaming es usar las clases XmlReader y XmlWriter de .NET junto con la clase XmlTranslatorStream proporcionada por BizTalk Server. Por ejemplo, la clase NamespaceTranslatorStream contenida en el ensamblado Microsoft.BizTalk.Pipeline.Components hereda de XmlTranslatorStream y se puede usar para reemplazar un espacio de nombres antiguo por uno nuevo en el documento XML contenido en la secuencia. Para usar esta funcionalidad dentro de un componente de canalización personalizado, puede encapsular el flujo de datos original de la parte del cuerpo del mensaje con una nueva instancia de la clase NamespaceTranslatorStream y devolver la última. De este modo, el mensaje entrante no se lee ni procesa dentro del componente de canalización, sino solo cuando un componente posterior lee la secuencia en la misma canalización o el Agente de mensajes lo consume finalmente antes de publicar el documento en el cuadro de mensajes de BizTalk Server.
En el ejemplo siguiente se muestra cómo usar esta funcionalidad.
public IBaseMessage Execute(IPipelineContext context, IBaseMessage message)
{
IBaseMessage outboundMessage = message;
try
{
if (context == null)
{
throw new ArgumentException(Resources.ContextIsNullMessage);
}
if (message == null)
{
throw new ArgumentException(Resources.InboundMessageIsNullMessage);
}
IBaseMessagePart bodyPart = message.BodyPart;
Stream stream = new NamespaceTranslatorStream(context,
bodyPart.GetOriginalDataStream(),
oldNamespace,
newNamespace);
context.ResourceTracker.AddResource(stream);
bodyPart.Data = stream;
}
catch (Exception ex)
{
if (message != null)
{
message.SetErrorInfo(ex);
}
throw ex;
}
return outboundMessage;
}
Uso de ResourceTracker en componentes de canalización personalizados
Un componente de canalización debe administrar la duración de los objetos que crea y realizar la recolección de elementos no utilizados en cuanto estos objetos ya no sean necesarios. Si el componente de canalización quiere que la duración de los objetos dure hasta el final de la ejecución de la canalización, debe agregar dichos objetos al seguimiento de recursos que la canalización puede capturar del contexto de canalización.
El seguimiento de recursos se usa para los siguientes tipos de objetos:
Objetos stream
objetos COM
Objetos IDisposable
El motor de mensajes garantiza que todos los recursos nativos que se agregan al seguimiento de recursos se liberan en un momento adecuado, que es después de que la canalización se ejecute completamente, independientemente de si se ha realizado correctamente o no. La duración de la instancia de Resource Tracker y los objetos que realiza el seguimiento se administran mediante el objeto de contexto de canalización. El contexto de canalización está disponible para todos los tipos de componentes de canalización a través de un objeto que implementa la interfaz IPipelineContext.
Por ejemplo, el siguiente fragmento de código es un ejemplo que muestra cómo usar la propiedad ResourceTracker en componentes de canalización personalizados. Para usar la propiedad ResourceTracker, el fragmento de código usa el siguiente parámetro
IPipelineContext.ResourceTracker.AddResource
. En este parámetro:La interfaz IPipelineContext define los métodos utilizados para acceder a todas las interfaces específicas del procesamiento de documentos.
La propiedad ResourceTracker hace referencia a IPipelineContext y se usa para realizar un seguimiento de los objetos que se eliminarán explícitamente al final del procesamiento de la canalización.
El método ResourceTracker.AddResource se usa para realizar un seguimiento de objetos COM, objetos descartables y secuencias, y siempre debe usarse dentro de un componente de canalización personalizado para cerrar explícitamente (secuencias), eliminar (objetos IDisposable) o liberar (objetos COM) estos tipos de recursos cuando se publica un mensaje en el Cuadro de mensajes de BizTalk.
public IBaseMessage Execute(IPipelineContext pContext, IBaseMessage pInMsg)
{
IBaseMessage outMessage = pContext.GetMessageFactory().CreateMessage();
IBaseMessagePart outMsgBodyPart = pContext.GetMessageFactory().CreateMessagePart();
outMsgBodyPart.Charset = Encoding.UTF8.WebName;
outMsgBodyPart.ContentType = "text/xml";
//Code to load message content in the MemoryStream object//
MemoryStream messageData = new MemoryStream();
//The MemoryStream needs to be closed after the whole pipeline has executed, thus adding it into ResourceTracker//
pContext.ResourceTracker.AddResource(messageData);
//Custom pipeline code to load message data into xmlPayload//
XmlDocument xmlPayLoad = new XmlDocument();
xmlPayLoad.Save(messageData);
messageData.Seek(0, SeekOrigin.Begin);
//The new stream is assigned to the message part’s data//
outMsgBodyPart.Data = messageData;
// Pipeline component logic here//
return outMessage;
}
Comparación de la carga de mensajes en canalizaciones mediante un enfoque en memoria y uso de un enfoque de streaming
La siguiente información se tomó del blog de Nic Barden, http://blogs.objectsharp.com/cs/blogs/nbarden/archive/2008/04/14/developing-streaming-pipeline-components-part-1.aspx (https://go.microsoft.com/fwlink/?LinkId=160228). En esta tabla se proporciona una comparación resumida de la carga de mensajes en canalizaciones mediante un enfoque en memoria y un enfoque de streaming.
Comparación de... | Streaming | En memoria |
---|---|---|
Uso de memoria por mensaje | Bajo, independientemente del tamaño del mensaje | Alto (varía en función del tamaño del mensaje) |
Clases comunes usadas para procesar datos XML | Derivaciones integradas y personalizadas de: XmlTranslatorStream, XmlReader y XmlWriter |
XmlDocument, XPathDocument, MemoryStream y VirtualStream |
Documentación | Deficiente: muchas clases de BizTalk no admitidas y no documentadas | Muy bueno: clases de .NET Framework |
Ubicación del código "Lógica de procesamiento" | - "Conexión" de lectores y secuencias a través del método Execute. - La ejecución real se produce en los lectores y flujos a medida que se leen los datos. |
Directamente desde el método Execute del componente de canalización. |
data | Se vuelve a crear en cada capa de ajuste a medida que se leen los datos. | Lea, modifique y escriba en cada componente antes de llamar al siguiente componente. |