Optimisation des performances du pipeline
Cette rubrique décrit des instructions pour optimiser les performances des pipelines dans une solution BizTalk Server.
Meilleures pratiques pour optimiser les performances des pipelines BizTalk Server
Étant donné que les composants de pipeline ont un impact significatif sur les performances (par exemple, un composant de pipeline direct fonctionne jusqu’à 30 % mieux qu’un composant de pipeline assembleur/désassembleur XML), assurez-vous que tous les composants de pipeline personnalisés fonctionnent de manière optimale avant de les implémenter dans votre déploiement. Réduisez le nombre de composants de pipeline dans vos pipelines personnalisés si vous souhaitez optimiser les performances globales de votre application BizTalk.
Vous pouvez également améliorer les performances globales en réduisant la fréquence de persistance des messages dans votre composant de pipeline et en codant votre composant pour réduire la redondance. Chaque assembly personnalisé et en particulier les artefacts susceptibles de perturber les performances, comme les composants de suivi personnalisés, doivent être testés séparément dans des conditions de charge élevée pour observer leur comportement lorsque le système fonctionne à pleine capacité et trouver les goulots d’étranglement possibles.
Si vous devez lire le message entrant à l’intérieur d’un composant de pipeline, évitez de charger l’intégralité du document en mémoire à l’aide d’un objet XmlDocument . La quantité d’espace nécessaire à un instance de la classe XmlDocument pour charger et créer une représentation en mémoire d’un document XML est jusqu’à 10 fois la taille réelle du message. Pour lire un message, vous devez utiliser un objet XmlTextReader avec un instance des classes suivantes :
VirtualStream (Microsoft.BizTalk.Streaming.dll) : le code source de cette classe se trouve à deux emplacements sous le Kit de développement logiciel (SDK) Pipelines, comme suit : SDK\Samples\Pipelines\ArbitraryXPathPropertyHandler et SDK\Samples\Pipelines\SchemaResolverComponent\SchemaResolverFlatFileDasm.
ReadOnlySeekableStream (Microsoft.BizTalk.Streaming.dll).
SeekAbleReadOnlyStream : le code source de cette classe se trouve à deux emplacements sous le Kit de développement logiciel (SDK) Pipelines, comme suit : SDK\Samples\Pipelines\ArbitraryXPathPropertyHandler et SDK\Samples\Pipelines\SchemaResolverComponent\SchemaResolverFlatFileDasm.
Utilisez les pipelines standard PassThruReceive et PassThruTransmit dans la mesure du possible. Ils ne contiennent aucun composant de pipeline et n’effectuent aucun traitement du message. Pour cette raison, ils garantissent des performances maximales lors de la réception ou de l’envoi de messages. Vous pouvez utiliser un pipeline PassThruReceive sur un emplacement de réception si vous devez publier un document binaire dans bizTalk MessageBox et un pipeline PassThruTransmit sur un port d’envoi si vous devez envoyer un message binaire. Vous pouvez également utiliser le pipeline PassThruTransmit sur un port d’envoi physique lié à une orchestration si le message a été mis en forme et est prêt à être transmis. Vous devez utiliser une approche différente si vous devez effectuer l’une des actions suivantes :
Promouvoir les propriétés dans le contexte d’un message XML ou fichier plat entrant.
Appliquer une carte à l’intérieur d’un emplacement de réception.
Appliquez une carte dans une orchestration qui s’abonne à un message.
Appliquez une carte sur un port d’envoi qui s’abonne à un message.
Pour effectuer l’une de ces actions, vous devez sonder et découvrir le type de document dans le pipeline de réception et affecter la valeur (namespace#root-name) à la propriété de contexte MessageType. Cette opération est généralement effectuée par un composant désassembleur tel que le composant Xml Désassembleur (XmlDasmComp) ou le composant de désassembleur de fichiers plats (FFDasmComp). Dans ce cas, vous devez utiliser un standard (pour instance pipeline XmlReceive) ou un pipeline personnalisé qui contient un composant standard ou un composant de désassembleur personnalisé.
Obtenez des ressources le plus tard possible et libérez-les le plus tôt possible. Par exemple, si vous devez accéder aux données d’une base de données, ouvrez la connexion le plus tard possible et fermez-la dès que possible. Utilisez l’instruction C# using pour libérer implicitement des objets jetables ou le bloc final d’une instruction try-catch-finally pour supprimer explicitement vos objets. Instrumentez votre code source pour simplifier le débogage de vos composants.
Éliminez les composants de vos pipelines qui ne sont pas strictement nécessaires pour accélérer le traitement des messages.
Dans un pipeline de réception, vous devez promouvoir des éléments dans le contexte de message uniquement si vous en avez besoin pour le routage des messages (orchestrations, ports d’envoi) ou la rétrogradation des propriétés de contexte de message (ports d’envoi).
Si vous devez inclure des métadonnées avec un message et que vous n’utilisez pas les métadonnées à des fins de routage ou de rétrogradation, utilisez la méthode IBaseMessageContext.Write au lieu de la méthode IBaseMessageContext.Promote .
Si vous devez extraire des informations d’un message à l’aide d’une expression XPath, évitez de charger l’intégralité du document en mémoire à l’aide d’un objet XmlDocument simplement pour utiliser les méthodes SelectNodes ou SelectSingleNode . Vous pouvez également utiliser les techniques décrites dans Optimisation de l’utilisation de la mémoire avec la diffusion en continu.
Utiliser la diffusion en continu pour réduire l’empreinte mémoire requise lors du chargement des messages dans les pipelines
Les techniques suivantes décrivent comment réduire l’empreinte mémoire d’un message lors du chargement du message dans un pipeline.
Utiliser ReadOnlySeekableStream et VirtualStream pour traiter un message à partir d’un composant de pipeline
Il est considéré comme une bonne pratique d’éviter de charger l’intégralité du message en mémoire à l’intérieur des composants du pipeline. Une approche préférable consiste à encapsuler le flux entrant avec une implémentation de flux personnalisé, puis à mesure que des demandes de lecture sont effectuées, l’implémentation de flux personnalisé lit le flux sous-jacent et encapsulé et traite les données au fur et à mesure de leur lecture (de manière de diffusion en continu pure). Cela peut être très difficile à implémenter et peut ne pas être possible, en fonction de ce qui doit être fait avec le flux. Dans ce cas, utilisez les classes ReadOnlySeekableStream et VirtualStream exposées par le Microsoft.BizTalk.Streaming.dll. Une implémentation de ceux-ci est également fournie dans Gestionnaire de propriétés XPath arbitraires (BizTalk Server Sample) (https://go.microsoft.com/fwlink/?LinkId=160069) dans le Kit de développement logiciel (SDK) BizTalk.ReadOnlySeekableStream garantit que le curseur peut être repositionné au début du flux. VirtualStream utilise un MemoryStream en interne, sauf si la taille dépasse un seuil spécifié, auquel cas il écrit le flux dans le système de fichiers. L’utilisation combinée de ces deux flux (en utilisant VirtualStream comme stockage persistant pour ReadOnlySeekableStream) offre à la fois des fonctionnalités de « recherche » et de « dépassement de capacité vers le système de fichiers ». Cela permet de traiter des messages volumineux sans charger l’intégralité du message en mémoire. Le code suivant peut être utilisé dans un composant de pipeline pour implémenter cette fonctionnalité.
int bufferSize = 0x280;
int thresholdSize = 0x100000;
Stream vStream = new VirtualStream(bufferSize, thresholdSize);
Stream seekStream = new ReadOnlySeekableStream(inboundStream, vStream, bufferSize);
Ce code fournit un « seuil de dépassement de capacité » en exposant les variables bufferSize et thresholdSize sur chaque emplacement de réception ou configuration de port d’envoi. Ce seuil de dépassement de capacité peut ensuite être ajusté par les développeurs ou les administrateurs pour différents types de messages et différentes configurations (par exemple, 32 bits et 64 bits).
Utilisation de XPathReader et XPathCollection pour extraire un objet IBaseMessage donné à partir d’un composant de pipeline personnalisé.
Si des valeurs spécifiques doivent être extraites d’un document XML, au lieu d’utiliser les méthodes SelectNodes et SelectSingleNode exposées par la classe XmlDocument, utilisez un instance de la classe XPathReader fournie par l’assembly Microsoft.BizTalk.XPathReader.dll, comme illustré dans l’exemple de code suivant.
Notes
Pour les messages plus petits, en particulier, l’utilisation d’un XmlDocument avec SelectNodes ou SelectSingleNode peut offrir de meilleures performances qu’avec XPathReader, mais XPathReader vous permet de conserver un profil de mémoire plate pour votre application.
Cet exemple montre comment utiliser XPathReader et XPathCollection pour extraire un objet IBaseMessage donné à partir d’un composant de pipeline personnalisé.
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;
}
Utiliser XMLReader et XMLWriter avec XMLTranslatorStream pour traiter un message à partir d’un composant de pipeline
Une autre méthode d’implémentation d’un composant de pipeline personnalisé qui utilise une approche de diffusion en continu consiste à utiliser les classes .NET XmlReader et XmlWriter conjointement avec la classe XmlTranslatorStream fournie par BizTalk Server. Par exemple, la classe NamespaceTranslatorStream contenue dans l’assembly Microsoft.BizTalk.Pipeline.Components hérite de XmlTranslatorStream et peut être utilisée pour remplacer un ancien espace de noms par un nouvel espace de noms dans le document XML contenu dans le flux. Pour utiliser cette fonctionnalité à l’intérieur d’un composant de pipeline personnalisé, vous pouvez encapsuler le flux de données d’origine du corps du message avec une nouvelle instance de la classe NamespaceTranslatorStream et retourner cette dernière. De cette façon, le message entrant n’est pas lu ou traité à l’intérieur du composant de pipeline, mais uniquement lorsque le flux est lu par un composant suivant dans le même pipeline ou qu’il est finalement consommé par l’agent de message avant de publier le document dans le BizTalk Server MessageBox.
L’exemple suivant montre comment utiliser cette fonctionnalité.
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;
}
Utilisation de ResourceTracker dans les composants de pipeline personnalisés
Un composant de pipeline doit gérer la durée de vie des objets qu’il crée et effectuer le garbage collection dès que ces objets ne sont plus nécessaires. Si le composant de pipeline souhaite que la durée de vie des objets dure jusqu’à la fin de l’exécution du pipeline, vous devez ajouter ces objets au suivi des ressources que votre pipeline peut extraire à partir du contexte de pipeline.
Le suivi de ressources est utilisé pour les types d’objets suivants :
Objets Stream
objets COM
Objets IDisposable
Le moteur de message garantit que toutes les ressources natives ajoutées au suivi des ressources sont libérées à un moment approprié, c’est-à-dire après l’exécution complète du pipeline, qu’il ait réussi ou échoué. La durée de vie de l’instance Resource Tracker et des objets qu’il suit est gérée par l’objet de contexte de pipeline. Le contexte de pipeline est mis à la disposition de tous les types de composants de pipeline via un objet qui implémente l’interface IPipelineContext.
Par exemple, l’extrait de code suivant est un exemple qui montre comment utiliser la propriété ResourceTracker dans les composants de pipeline personnalisés. Pour utiliser la propriété ResourceTracker, l’extrait de code utilise le paramètre
IPipelineContext.ResourceTracker.AddResource
suivant . Dans ce paramètre :L’interface IPipelineContext définit les méthodes utilisées pour accéder à toutes les interfaces spécifiques au traitement de document.
La propriété ResourceTracker fait référence à IPipelineContext et est utilisée pour effectuer le suivi des objets qui seront explicitement supprimés à la fin du traitement du pipeline.
La méthode ResourceTracker.AddResource est utilisée pour effectuer le suivi des objets COM, objets jetables et flux, et doit toujours être utilisée à l’intérieur d’un composant de pipeline personnalisé pour fermer explicitement (flux), supprimer (objets IDisposable) ou libérer (objets COM) ces types de ressources lorsqu’un message est publié dans bizTalk MessageBox.
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;
}
Comparaison du chargement de messages dans des pipelines à l’aide d’une approche en mémoire et d’une approche de diffusion en continu
Les informations suivantes ont été extraites du 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). Ce tableau fournit une comparaison résumée du chargement de messages dans des pipelines à l’aide d’une approche en mémoire et d’une approche de diffusion en continu.
Comparaison de... | Diffusion en continu | En mémoire |
---|---|---|
Utilisation de la mémoire par message | Faible, quelle que soit la taille du message | Élevé (varie en fonction de la taille du message) |
Classes courantes utilisées pour traiter les données XML | Dérivations intégrées et personnalisées de : XmlTranslatorStream, XmlReader et XmlWriter |
XmlDocument, XPathDocument, MemoryStream et VirtualStream |
Documentation | Médiocre : de nombreuses classes BizTalk non prises en charge et non documentées | Très bon - Classes .NET Framework |
Emplacement du code « Logique de traitement » | - Connecter des lecteurs et des flux via la méthode Execute. - L’exécution réelle se produit dans les lecteurs et les flux à mesure que les données sont lues. |
Directement à partir de la méthode Execute du composant de pipeline. |
Données | Recréé à chaque couche d’habillage à mesure que les données sont lues. | Lisez, modifiez et écrivez au niveau de chaque composant avant l’appel du composant suivant. |