Optimieren der Leistung der Pipeline
In diesem Thema werden Richtlinien zum Optimieren der Leistung von Pipelines in einer BizTalk Server-Lösung beschrieben.
Bewährte Methoden zum Optimieren der Leistung BizTalk Server Pipelines
Da Pipelinekomponenten einen erheblichen Einfluss auf die Leistung haben (z. B. eine Passthrough-Pipelinekomponente schneidet bis zu 30 Prozent besser ab als eine XML-Assembler-/Disassemblerpipelinekomponente), stellen Sie sicher, dass alle benutzerdefinierten Pipelinekomponenten optimal funktionieren, bevor Sie sie in Ihrer Bereitstellung implementieren. Minimieren Sie die Anzahl der Pipelinekomponenten in Ihren benutzerdefinierten Pipelines, wenn Sie die Gesamtleistung Ihrer BizTalk-Anwendung maximieren möchten.
Sie können auch die Gesamtleistung verbessern, indem Sie die Nachrichtenpersistenzfrequenz in Ihrer Pipelinekomponente reduzieren und Ihre Komponente codieren, um redundanz zu minimieren. Jede benutzerdefinierte Assembly und insbesondere Artefakte, die möglicherweise die Leistung beeinträchtigen könnten, wie z. B. benutzerdefinierte Nachverfolgungskomponenten, sollten separat unter Bedingungen mit hoher Last getestet werden, um ihr Verhalten zu beobachten, wenn das System mit voller Kapazität arbeitet, und um mögliche Engpässe zu finden.
Wenn Sie die eingehende Nachricht in einer Pipelinekomponente lesen müssen, vermeiden Sie es, das gesamte Dokument mithilfe eines XmlDocument-Objekts in den Arbeitsspeicher zu laden. Der Speicherplatz, der von einem instance der XmlDocument-Klasse benötigt wird, um eine Im-Speicher-Darstellung eines XML-Dokuments zu laden und zu erstellen, ist bis zu 10 Mal so groß wie die tatsächliche Nachrichtengröße. Um eine Nachricht zu lesen, sollten Sie ein XmlTextReader-Objekt zusammen mit einem instance der folgenden Klassen verwenden:
VirtualStream (Microsoft.BizTalk.Streaming.dll): Der Quellcode für diese Klasse befindet sich an zwei Speicherorten unter dem Pipelines SDK: SDK\Samples\Pipelines\ArbitraryXPathPropertyHandler und SDK\Samples\Pipelines\SchemaResolverComponent\SchemaResolverFlatFileDasm.
ReadOnlySeekableStream (Microsoft.BizTalk.Streaming.dll).
SeekAbleReadOnlyStream : Der Quellcode für diese Klasse befindet sich an zwei Speicherorten unter dem Pipelines SDK: SDK\Samples\Pipelines\ArbitraryXPathPropertyHandler und SDK\Samples\Pipelines\SchemaResolverComponent\SchemaResolverFlatFileDasm.
Verwenden Sie nach Möglichkeit die Standardpipelines PassThruReceive und PassThruTransmit. Sie enthalten keine Pipelinekomponente und führen keine Verarbeitung der Nachricht durch. Aus diesem Grund sorgen sie für maximale Leistung beim Empfangen oder Senden von Nachrichten. Sie können eine PassThruReceive-Pipeline an einem Empfangsspeicherort verwenden, wenn Sie ein binäres Dokument in der BizTalk MessageBox und eine PassThruTransmit-Pipeline an einem Sendeport veröffentlichen müssen, wenn Sie eine binäre Nachricht senden müssen. Sie können die PassThruTransmit-Pipeline auch an einem physischen Sendeport verwenden, der an eine Orchestrierung gebunden ist, wenn die Nachricht formatiert wurde und zur Übertragung bereit ist. Sie müssen einen anderen Ansatz verwenden, wenn Sie eine der folgenden Aktionen ausführen müssen:
Höherstufen von Eigenschaften im Kontext einer eingehenden XML- oder Flatfilenachricht.
Wenden Sie eine Karte innerhalb eines Empfangsstandorts an.
Wenden Sie eine Zuordnung in einer Orchestrierung an, die eine Nachricht abonniert.
Wenden Sie eine Karte auf einen Sendeport an, der eine Nachricht abonniert.
Um eine dieser Aktionen auszuführen, müssen Sie den Dokumenttyp in der Empfangspipeline testen und ermitteln und der MessageType-Kontexteigenschaft den Wert (namespace#root-name) zuweisen. Dieser Vorgang wird in der Regel durch eine Disassemblerkomponente wie die Xml Disassembler-Komponente (XmlDasmComp) oder die Flatfile-Disassemblerkomponente (FFDasmComp) ausgeführt. In diesem Fall müssen Sie einen Standard (für instance XmlReceive-Pipeline) oder eine benutzerdefinierte Pipeline verwenden, die eine Standard- oder benutzerdefinierte Disassemblerkomponente enthält.
Beziehen Sie Ressourcen so spät wie möglich, und geben Sie sie so früh wie möglich frei. Wenn Sie beispielsweise auf Daten in einer Datenbank zugreifen müssen, öffnen Sie die Verbindung so spät wie möglich, und schließen Sie sie so schnell wie möglich. Verwenden Sie die C#-Using-Anweisung, um einwegige Objekte implizit freizugeben, oder den endgültigen Block einer try-catch-finally-Anweisung, um Ihre Objekte explizit zu entsorgen. Instrumentieren Sie Ihren Quellcode, um das Debuggen Ihrer Komponenten zu vereinfachen.
Entfernen Sie alle Komponenten aus Ihren Pipelines, die nicht unbedingt erforderlich sind, um die Nachrichtenverarbeitung zu beschleunigen.
Innerhalb einer Empfangspipeline sollten Sie Elemente nur dann in den Nachrichtenkontext höherstufen, wenn Sie sie für das Nachrichtenrouting (Orchestrierungen, Sendeports) oder für die Herabstufung von Nachrichtenkontexteigenschaften (Sendeports) benötigen.
Wenn Sie Metadaten in eine Nachricht einschließen müssen und die Metadaten nicht für Routing- oder Demotionszwecke verwenden, verwenden Sie die IBaseMessageContext.Write-Methode anstelle der IBaseMessageContext.Promote-Methode .
Wenn Sie Mithilfe eines XPath-Ausdrucks Informationen aus einer Nachricht extrahieren müssen, vermeiden Sie es, das gesamte Dokument mithilfe eines XmlDocument-Objekts in den Arbeitsspeicher zu laden, nur um die SelectNodes - oder SelectSingleNode-Methoden zu verwenden. Alternativ können Sie die unter Optimieren der Speichernutzung mit Streaming beschriebenen Techniken verwenden.
Verwenden des Streamings, um den erforderlichen Arbeitsspeicherbedarf beim Laden von Nachrichten in Pipelines zu minimieren
In den folgenden Techniken wird beschrieben, wie Der Arbeitsspeicherbedarf einer Nachricht beim Laden der Nachricht in eine Pipeline minimiert wird.
Verwenden von ReadOnlySeekableStream und VirtualStream zum Verarbeiten einer Nachricht aus einer Pipelinekomponente
Es wird als bewährte Methode angesehen, zu vermeiden, dass die gesamte Nachricht in den Arbeitsspeicher in Pipelinekomponenten geladen wird. Ein bevorzugter Ansatz besteht darin, den eingehenden Stream mit einer benutzerdefinierten Streamimplementierung zu umschließen, und dann liest die benutzerdefinierte Streamimplementierung den zugrunde liegenden, umschlossenen Stream und verarbeitet die Daten beim Lesen (auf reine Streaming-Weise). Dies kann sehr schwer zu implementieren sein und ist möglicherweise nicht möglich, je nachdem, was mit dem Stream getan werden muss. Verwenden Sie in diesem Fall die ReadOnlySeekableStream - und VirtualStream-Klassen , die vom Microsoft.BizTalk.Streaming.dll verfügbar gemacht werden. Eine Implementierung dieser wird auch im Handler für beliebige XPath-Eigenschaften (BizTalk Server Beispiel) (https://go.microsoft.com/fwlink/?LinkId=160069) im BizTalk SDK bereitgestellt.ReadOnlySeekableStream stellt sicher, dass der Cursor am Anfang des Datenstroms neu positioniert werden kann. Der VirtualStream verwendet intern einen MemoryStream, es sei denn, die Größe überschreitet einen angegebenen Schwellenwert. In diesem Fall schreibt er den Stream in das Dateisystem. Die Verwendung dieser beiden Datenströme in Kombination (unter Verwendung von VirtualStream als persistenter Speicher für ReadOnlySeekableStream) bietet sowohl "Seekability" als auch "Overflow to File System"-Funktionen. Dadurch können große Nachrichten verarbeitet werden, ohne dass die gesamte Nachricht in den Arbeitsspeicher geladen wird. Der folgende Code kann in einer Pipelinekomponente verwendet werden, um diese Funktionalität zu implementieren.
int bufferSize = 0x280;
int thresholdSize = 0x100000;
Stream vStream = new VirtualStream(bufferSize, thresholdSize);
Stream seekStream = new ReadOnlySeekableStream(inboundStream, vStream, bufferSize);
Dieser Code stellt einen "Überlaufschwellenwert" bereit, indem pufferSize- und thresholdSize-Variablen für jeden Empfangsspeicherort oder jede Sendeportkonfiguration verfügbar sind. Dieser Überlaufschwellenwert kann dann von Entwicklern oder Administratoren für verschiedene Nachrichtentypen und unterschiedliche Konfigurationen (z. B. 32-Bit im Vergleich zu 64-Bit) angepasst werden.
Verwenden von XPathReader und XPathCollection, um ein bestimmtes IBaseMessage-Objekt aus einer benutzerdefinierten Pipelinekomponente zu extrahieren.
Wenn bestimmte Werte aus einem XML-Dokument abgerufen werden müssen, verwenden Sie anstelle der SelectNodes- und SelectSingleNode-Methoden, die von der XmlDocument-Klasse verfügbar gemacht werden, eine instance der XPathReader-Klasse, die von der Microsoft.BizTalk.XPathReader.dll Assembly bereitgestellt wird, wie im folgenden Codebeispiel veranschaulicht.
Hinweis
Insbesondere bei kleineren Nachrichten kann die Verwendung eines XmlDocument mit SelectNodes oder SelectSingleNode eine bessere Leistung als die Verwendung von XPathReader bieten, aber XPathReader ermöglicht es Ihnen, ein Flaches Speicherprofil für Ihre Anwendung beizubehalten.
In diesem Beispiel wird gezeigt, wie Sie XPathReader und XPathCollection verwenden, um ein bestimmtes IBaseMessage-Objekt aus einer benutzerdefinierten Pipelinekomponente zu extrahieren.
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;
}
Verwenden von XMLReader und XMLWriter mit XMLTranslatorStream zum Verarbeiten einer Nachricht aus einer Pipelinekomponente
Eine weitere Methode zum Implementieren einer benutzerdefinierten Pipelinekomponente, die einen Streamingansatz verwendet, ist die Verwendung der .NET XmlReader- und XmlWriter-Klasse in Verbindung mit der von BizTalk Server bereitgestellten XmlTranslatorStream-Klasse. Beispielsweise erbt die NamespaceTranslatorStream-Klasse , die in der Microsoft.BizTalk.Pipeline.Components-Assembly enthalten ist, von XmlTranslatorStream und kann verwendet werden, um einen alten namespace durch einen neuen im XML-Dokument im Stream enthaltenen XML-Dokument zu ersetzen. Um diese Funktionalität in einer benutzerdefinierten Pipelinekomponente zu verwenden, können Sie den ursprünglichen Datenstrom des Nachrichtentextteils mit einem neuen instance der NamespaceTranslatorStream-Klasse umschließen und letzteres zurückgeben. Auf diese Weise wird die eingehende Nachricht nicht innerhalb der Pipelinekomponente gelesen oder verarbeitet, sondern nur, wenn der Stream von einer nachfolgenden Komponente in derselben Pipeline gelesen oder schließlich vom Nachrichten-Agent verwendet wird, bevor das Dokument im BizTalk Server MessageBox veröffentlicht wird.
Im folgenden Beispiel wird veranschaulicht, wie Diese Funktionalität verwendet wird.
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;
}
Verwenden von ResourceTracker in benutzerdefinierten Pipelinekomponenten
Eine Pipelinekomponente sollte die Lebensdauer der von ihr erstellten Objekte verwalten und eine Garbage Collection ausführen, sobald diese Objekte nicht mehr erforderlich sind. Wenn die Pipelinekomponente möchte, dass die Lebensdauer der Objekte bis zum Ende der Pipelineausführung dauert, müssen Sie dem Ressourcentracker solche Objekte hinzufügen, die Ihre Pipeline möglicherweise aus dem Pipelinekontext abruft.
Der Ressourcennachverfolgung wird für die folgenden Objekttypen verwendet:
Streamobjekte
COM-Objekte
IDisposable-Objekte
Die Nachrichten-Engine stellt sicher, dass alle nativen Ressourcen, die dem Ressourcentracker hinzugefügt werden, zu einem geeigneten Zeitpunkt freigegeben werden, d. h. nachdem die Pipeline vollständig ausgeführt wurde, unabhängig davon, ob sie erfolgreich war oder fehlgeschlagen ist. Die Lebensdauer des Resource Tracker-instance und die Objekte, die er nachverfolgt, werden vom Pipelinekontextobjekt verwaltet. Der Pipelinekontext wird für alle Typen von Pipelinekomponenten über ein Objekt zur Verfügung gestellt, das die IPipelineContext-Schnittstelle implementiert.
Der folgende Codeausschnitt ist beispielsweise ein Beispiel, das veranschaulicht, wie die ResourceTracker-Eigenschaft in benutzerdefinierten Pipelinekomponenten verwendet wird. Um die ResourceTracker-Eigenschaft zu verwenden, verwendet der Codeausschnitt den folgenden Parameter
IPipelineContext.ResourceTracker.AddResource
. In diesem Parameter:Die IPipelineContext-Schnittstelle definiert die Methoden, die für den Zugriff auf alle dokumentverarbeitungsspezifischen Schnittstellen verwendet werden.
Die ResourceTracker-Eigenschaft verweist auf IPipelineContext und wird verwendet, um Objekte nachzuverfolgen, die am Ende der Pipelineverarbeitung explizit verworfen werden.
Die ResourceTracker.AddResource-Methode wird verwendet, um COM-Objekte, Wegwerfobjekte und Streams nachzuverfolgen. Sie sollte immer innerhalb einer benutzerdefinierten Pipelinekomponente verwendet werden, um diese Ressourcentypen explizit zu schließen (Streams), zu entsorgen (IDisposable-Objekte) oder (COM-Objekte) freizugeben, wenn eine Nachricht in der BizTalk MessageBox veröffentlicht wird.
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;
}
Vergleich des Ladens von Nachrichten in Pipelines mit einem In-Memory-Ansatz und einem Streamingansatz
Die folgenden Informationen stammen aus Nic Bardens Blog (http://blogs.objectsharp.com/cs/blogs/nbarden/archive/2008/04/14/developing-streaming-pipeline-components-part-1.aspxhttps://go.microsoft.com/fwlink/?LinkId=160228). Diese Tabelle enthält einen zusammengefassten Vergleich des Ladens von Nachrichten in Pipelines mithilfe eines In-Memory-Ansatzes und eines Streamingansatzes.
Vergleich von... | Streaming | Im Arbeitsspeicher |
---|---|---|
Arbeitsspeicherauslastung pro Nachricht | Niedrig, unabhängig von der Nachrichtengröße | Hoch (variiert je nach Nachrichtengröße) |
Gängige Klassen, die zum Verarbeiten von XML-Daten verwendet werden | Integrierte und benutzerdefinierte Ableitungen von: XmlTranslatorStream, XmlReader und XmlWriter |
XmlDocument, XPathDocument, MemoryStream und VirtualStream |
Dokumentation | Schlecht – viele nicht unterstützte und nicht dokumentierte BizTalk-Klassen | Sehr gut - .NET Framework Klassen |
Speicherort des Codes "Verarbeitungslogik" | – "Wire up"-Leser und Datenströme über die Execute-Methode. – Die tatsächliche Ausführung erfolgt in den Readern und Streams, wenn die Daten gelesen werden. |
Direkt aus der Execute-Methode der Pipelinekomponente. |
Daten | Wird auf jeder Umbruchebene neu erstellt, während Daten gelesen werden. | Lesen, ändern und schreiben Sie jede Komponente ein, bevor die nächste Komponente aufgerufen wird. |