Austauschmuster für Sendeadapter
Sendeadapter sind von der BizTalk-Messaging-Engine übermittelte Nachrichten, die über die Verbindung übertragen werden sollen. Diese Nachrichten können mithilfe eines uni- oder bidirektionalen Austauschmusters gesendet werden. Bidirektionale Austauschmuster verarbeitende Adapter werden als Adapter vom Typ "Antwort anfragen" bezeichnet.
Blockierende und nicht blockierende Übertragungen
Die Messaging-Engine sendet Nachrichten an den Sendeadapter mithilfe der IBTTransmitter.TransmissionMessage-Methode oder der IBTTransmitterBatch.TransmissionMessage-Methode , je nachdem, ob der Adapter batchfähig ist. Beide Methoden haben einen booleschen Rückgabewert, der angibt, auf welche Weise der Adapter die Nachricht übertragen hat. Wenn der Adapter zurückgibt true
, hat er die Nachricht vollständig gesendet, bevor er zurückgibt. In diesem Fall löscht die Messaging-Engine die Nachricht für den Adapter aus der MessageBox-Datenbank. Wenn der Adapter zurückgibt false
, hat er die Nachrichtenübertragung gestartet und vor Abschluss der Übertragung zurückgegeben. In diesem Fall ist der Adapter nicht nur für das Löschen der Nachricht aus seiner Anwendungswarteschlange zuständig, sondern auch für die Verarbeitung von Übertragungsfehlern, aufgrund deren die Nachricht erneut übertragen oder angehalten werden muss.
Der zurückgegebene false
Adapter ist ein nicht blockierender Aufruf, was bedeutet, dass der TransmissionMessage-Implementierungscode den aufrufenden Thread der Messaging-Engine nicht blockiert. Der Adapter fügt die Nachricht einfach der Warteschlange im Arbeitsspeicher hinzu, die zur Übertragung bereit steht, und gibt anschließend einen Wert zurück. Der Adapter sollte einen eigenen Threadpool besitzen, der die Warteschlange im Arbeitsspeicher bedient, die Nachricht überträgt und dann die Engine über das Ergebnis informiert.
Die Threads der Messaging-Engine sind im Gegensatz zu Threads, die zum Senden von Daten über eine Verbindung verwendet werden, hauptsächlich CPU-abhängig. Eine Mischung dieser beiden Threadtypen wirkt sich negativ auf die Leistung aus. Nicht blockierende Sendevorgänge ermöglichen eine Trennung dieser beiden Threadtypen und zeigen im Vergleich zu blockierenden Aufrufen eine wesentlich bessere Leistung.
Im folgenden Diagramm wird der Threadpool des Adapters gezeigt, der (möglicherweise) häufig durch E/A-Operationen gebunden wird. Der Threadpool der BizTalk Server-Messaging-Engine wird häufiger durch die CPU-Verarbeitung gebunden. Wenn Sie zwei verschiedene Threadpools verwenden und es vermeiden, die gleichen Threadtypen zu mischen, kann das System effizienter arbeiten.
Leistungstipp: Um eine optimale Leistung zu erzielen, sollten Sendeadapter nicht blockierend und batchfähig sein. Nachdem der BizTalk-Dateiadapter in nicht blockierend und für die Unterstützung von Batches geändert wurde, steigerte sich bei unseren Tests die Leistung um das Dreifache.
Tipp zur Problembehandlung: Das Blockieren von Übertragungen kann zu einer Leistungsminderung eines gesamten Hosts instance führen. Wenn der Adapter übermäßige Blockierung in TransmissionMessage ausführt, wird verhindert, dass Enginethreads Nachrichten an andere Adapter senden.
Nicht in Batches verarbeitete Sendevorgänge
Adapter, die nicht batchfähig sind, sollten IBTTransmitter implementieren, wie unter Schnittstellen für einen asynchronen Sendeadapter beschrieben. Für jede Nachricht, die der Adapter zum Übertragen der Messaging-Engine benötigt, ruft IBTTransmitter.TransmissionMessage auf. Das folgende Diagramm zur Objektinteraktion veranschaulicht die typische Herangehensweise an die Übertragung von Nachrichten. Folgende Schritte werden ausgeführt:
Die Engine sendet die Nachricht an den Adapter.
Der Adapter stellt die Nachricht in die Warteschlange im Arbeitsspeicher, die zur Übertragung bereit steht.
Ein Thread aus dem Threadpool des Adapters entfernt die Nachricht aus der Warteschlange, liest die Konfiguration für die Nachricht und überträgt die Nachricht über die Verbindung.
Der Adapter erhält von der Engine einen neuen Batch.
Der Adapter ruft DeleteMessage im Batch auf und übergibt die soeben übertragene Nachricht.
Der Adapter ruft Done für den Batch auf.
Die Engine verarbeitet den Batch und löscht die Nachricht aus der Anwendungswarteschlange.
Das Modul ruft den Adapter zurück, um ihn über das Ergebnis des DeleteMessage-Vorgangs zu benachrichtigen.
Das folgende Diagramm zur Objektinteraktion veranschaulicht, wie der Adapter eine einzelne Nachricht aus der Anwendungswarteschlange löscht. Idealerweise verarbeitet der Adapter Nachrichtenvorgänge in Batches, anstatt jede Nachricht einzeln zu behandeln.
In Batches verarbeitete Sendevorgänge
Adapter, die batchfähig sind, sollten IBTBatchTransmitter und IBTTransmitterBatch implementieren, wie unter Schnittstellen für Sendeadapter beschrieben. Wenn das Modul Nachrichten für den Adapter zum Übertragen enthält, erhält das Modul einen neuen Batch vom Adapter, indem IBTBatchTransmitter.GetBatch aufgerufen wird. Der Adapter gibt ein neues Batchobjekt zurück, das IBTTransmitterBatch implementiert. Das Modul startet dann den Batch, indem IBTTransmitterBatch.BeginBatch aufgerufen wird. Diese API besitzt einen Ausgabeparameter, mit dem der Adapter die maximale Anzahl an Nachrichten festlegen kann, die er für den Batch akzeptiert. Der Adapter kann optional eine DTC-Transaktion zurückgeben. Das Modul ruft dann IBTTransmitterBatch.TransmitMessage einmal auf, damit jede ausgehende Nachricht dem Batch hinzugefügt wird. Die Anzahl dieser Aufrufe ist größer als Null aber nicht größer als die maximale Größe des Batches, die vom Adapter angegeben wird. Wenn alle Nachrichten dem Batch hinzugefügt wurden, ruft der Adapter IBTTransmitterBatch.Done auf. Zu diesem Zeitpunkt stellt der Adapter normalerweise alle Nachrichten im Batch in seine Warteschlange im Arbeitsspeicher. Der Adapter überträgt die Nachrichten von einem oder mehreren Threads seines eigenen Threadpools, wie im Falle von Adaptern, die keine Batches unterstützen. Der Adapter benachrichtigt die Engine anschließend über das Ergebnis der Übertragung.
Das folgende Diagramm zur Objektinteraktion veranschaulicht die Übertragung zweier Nachrichten von einem als Batch ausgeführten Sendeadapter.
Verarbeitung von Übertragungsfehlern
Die empfohlene Semantik für Übertragungsfehler wird in der nachfolgenden Abbildung dargestellt. Hierbei handelt es sich lediglich um Empfehlungen, die von der Messaging-Engine nicht erzwungen werden. Sie können einen Adapter entwickeln, der von diesen Richtlinien abweicht, wenn Sie gute Gründe dafür haben. Gehen Sie in diesem Fall jedoch vorsichtig vor. Im Allgemeinen sollte ein Adapter beispielsweise Nachrichten stets in den sekundären Transport verschieben, nachdem alle Wiederholungen ausgeschöpft wurden.
Häufig benötigt ein Transport mehr Wiederholungen als konfiguriert wurden. Obwohl dieser Umstand leicht abweicht, wird er als akzeptabel betrachtet, da die Flexibilität der Transportebene erhöht wird. Im Allgemeinen sind die von der Messaging-Engine bereitgestellten APIs so entworfen, dass der Adapter die maximale Kontrolle erhält, wann immer dies möglich ist. Mit dieser Kontrolle steigt auch der Grad der Verantwortung.
Der Adapter bestimmt die Anzahl der für eine Nachricht verfügbaren Wiederholungen, indem er die Systemkontexteigenschaft RetryCount überprüft. Der Adapter ruft die API für jeden Wiederholungsversuch einmal auf und übergibt die Nachricht, die erneut gesendet werden soll. Zusammen mit der Nachricht wird der Zeitstempel übergeben. Dieser gibt an, wann die Engine die Nachricht an den Adapter zurücksenden soll. Dieser Wert sollte in der Regel die aktuelle Uhrzeit plus der Wert von RetryInterval sein. RetryInterval ist eine Systemkontexteigenschaft, deren Einheiten Minuten sind. Sowohl retryCount als auch RetryInterval im Nachrichtenkontext sind die Werte, die für den Sendeport konfiguriert sind. Betrachten Sie zum Beispiel eine dezentral skalierte Bereitstellung mit Instanzen desselben BizTalk-Hosts, die auf mehreren Computern bereitgestellt sind. Wenn die Nachricht gesendet wird, nachdem das Wiederholungsintervall abgelaufen ist, wird die Nachricht möglicherweise an irgendeine Hostinstanz auf irgendeinem Computer gesendet, auf dem die Instanzen konfiguriert wurden. Aus diesem Grunde sollte der Adapter keinen Status enthalten, der mit einer Nachricht verknüpft ist, die für den Wiederholungsversuch verwendet werden soll, da es keine Gewährleistung gibt, dass dieselbe Instanz des Adapters zu einem späteren Zeitpunkt für die Übertragung zuständig ist.
Der Adapter sollte nur dann versuchen, die Nachrichten in den sekundären Transport zu verschieben, wenn die Wiederholungsanzahl kleiner oder gleich Null ist. Ein Versuch, die Nachrichten in den sekundären Transport zu verschieben, schlägt fehl, wenn für den Port kein sekundärer Transport konfiguriert wurde. Ist dies der Fall, sollte die Nachricht angehalten werden.
Das folgende Codefragment veranschaulicht, wie Wiederholungsanzahl und -intervall anhand des Nachrichtenkontextes ermittelt werden. Außerdem wird der nachfolgende erneute Sendevorgang oder das Verschieben in den sekundären Transport dargestellt.
using Microsoft.XLANGs.BaseTypes;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.TransportProxy.Interop;
…
// RetryCount and RetyInterval are system context properties...
private static readonly PropertyBase RetryCountProperty =
new BTS.RetryCount();
private static readonly PropertyBase RetryIntervalProperty =
new BTS.RetryInterval();
public void HandleRetry(IBaseMessage msg, IBTTransportBatch batch)
{
int retryCount = 0;
int retryInterval = 0;
// Get the RetryCount and RetryInterval off the msg ctx...
GetMessageRetryState(msg, out retryCount, out retryInterval);
// If we have retries available resubmit, else move to
// backup transport...
if ( retryCount > 0 )
batch.Resubmit(
msg, DateTime.Now.AddMinutes(retryInterval));
else
batch.MoveToNextTransport(msg);
}
public void GetMessageRetryState(
IBaseMessage msg,
out int retryCount,
out int retryInterval )
{
retryCount = 0;
retryInterval = 0;
object obj = msg.Context.Read(
RetryCountProperty.Name.Name,
RetryCountProperty.Name.Namespace);
if ( null != obj )
retryCount = (int)obj;
obj = msg.Context.Read(
RetryIntervalProperty.Name.Name,
RetryIntervalProperty.Name.Namespace);
if ( null != obj )
retryInterval = (int)obj;
}
Auslösen einer Ausnahme über "TransmitMessage"
Wenn der Adapter eine Ausnahme für eine der APIs IBTTransmitter.TransmissionMessage, IBTTransmitterBatch.TransmitMessage, IBTTransmitterBatch.Done auslöst, behandelt das Modul die Übertragung der betreffenden Nachrichten als Übertragungsfehler und führt die entsprechende Aktion für die Nachricht aus, wie unter Behandeln von Adapterfehlern beschrieben.
Im Falle von Sendeadaptern, die Batches unterstützen, wird nach dem Auslösen einer Ausnahme auf der TransmitMessage API der gesamte Batch gelöscht, und für alle Nachrichten in diesem Batch werden die standardmäßigen Übertragungsfehleraktionen durchgeführt.
Antwort anfragen
Bidirektionale Sendeadapter unterstützen normalerweise sowohl uni- als auch bidirektionale Übertragungen. Der Sendeadapter bestimmt, ob die Nachricht als unidirektionales oder bidirektionales Senden übertragen werden soll, indem die Systemkontexteigenschaft IsSolicitResponse im Nachrichtenkontext überprüft wird.
Der folgende Codeausschnitt stellt dies dar:
private bool portIsTwoWay = false;
private static readonly PropertyBase IsSolicitResponseProperty= new BTS.IsSolicitResponse();
...
// Port is one way or two way...
object obj = this.message.Context.Read(
IsSolicitResponseProperty.Name.Name,
IsSolicitResponseProperty.Name.Namespace);
if ( null != obj )
this.portIsTwoWay = (bool)obj;
Während einer Übertragung vom Typ "Antwort anfragen" überträgt der Adapter die Anfragenachricht. Anschließend übermittelt er die zugehörige Antwort und weist die Messaging-Engine an, die ursprüngliche Anfragenachricht aus der MessageBox-Datenbank zu löschen. Das Löschen der Nachricht aus der Anwendungswarteschlange sollte im gleichen Transportproxybatch wie die Übermittlung der Antwortnachricht durchgeführt werden. Auf diese Weise wird sichergestellt, dass Löschen und Übermitteln der Antwort unteilbar sind. Um eine vollständige Unteilbarkeit zu erreichen, sollte der Adapter eine DTC-Transaktion verwenden. Hierbei befinden sich die Übertragung der Anfragenachricht an eine Transaktionen unterstützende Ressource, die Übermittlung der Antwortnachricht und das Löschen der Anfragenachricht alle im Kontext derselben DTC-Transaktion. Auch hier empfehlen wir, die Anfragenachricht mithilfe eines nicht blockierenden Sendevorgangs zu übertragen.
Das folgende Codefragment illustriert die wichtigsten Aspekte eines bidirektionalen Sendevorgangs. Wenn das Modul IBTTransmitter.TransmitMessage aufruft, quent der Adapter die Nachricht an eine In-Memory-Warteschlange zu übertragen. Der Adapter gibt zurück false
, um anzugeben, dass er einen nicht blockierenden Sendevorgang ausführt. Der Threadpool des Adapters (WorkerThreadThunk) verwaltet die In-Memory-Warteschlange und dequediert eine Nachricht, um sie an eine Hilfsmethode zu übergeben. Diese Methode ist für das Senden der Anfragenachricht und Empfangen der Antwortnachricht verantwortlich. (Die Implementierung dieser Hilfsmethode wird in diesem Thema nicht behandelt.) Die Antwortnachricht wird an die Engine übermittelt und die Anfragenachricht wird aus der Anwendungswarteschlange gelöscht.
using System.Collections;
using Microsoft.XLANGs.BaseTypes;
using Microsoft.BizTalk.Message.Interop;
using Microsoft.BizTalk.TransportProxy.Interop;
static private Queue _transmitQueue = new Queue();
static private IBTTransportProxy _transportProxy = null;
// IBTTransmitter...
public bool TransmitMessage(IBaseMessage msg)
{
// Add message to the transmit queue...
lock(_transmitQueue.SyncRoot)
{
_transmitQueue.Enqueue(msg);
}
return false;
}
// Threadpool worker thread...
private void WorkerThreadThunk()
{
try
{
IBaseMessage solicitMsg = null;
lock(_transmitQueue.SyncRoot)
{
solicitMsg =
(IBaseMessage)_transmitQueue.Dequeue();
}
IBaseMessage responseMsg = SendSolicitResponse(
solicitMsg );
Callback cb = new Callback();
IBTTransportBatch batch = _transportProxy.GetBatch(
cb, null);
batch.SubmitResponseMessage( solicitMsg, responseMsg );
batch.DeleteMessage( solicitMsg );
batch.Done(null);
}
catch(Exception)
{
// Handle failure....
}
}
static private IBaseMessage SendSolicitResponse(
IBaseMessage solicitMsg )
{
// Helper method to send solicit message and receive
// response message...
IBaseMessage responseMsg = null;
return responseMsg;
}
Dynamische Sendevorgänge
Dynamischen Sendeports sind keine Adapterkonfigurationen zugeordnet. Stattdessen verwenden sie die Handlerkonfiguration für alle standardmäßigen Eigenschaften, die der Adapter auf einem dynamischen Port übertragen muss. Beispiel: Ein HTTP-Adapter muss einen Proxy verwenden und Anmeldeinformationen bereitstellen. Benutzername, Kennwort und Port können in der Handlerkonfiguration angegeben sein, die der Adapter zur Laufzeit zwischenspeichert.
Damit die Engine den Transport bestimmen kann, über den eine dynamische Nachricht gesendet werden soll, wird dem OutboundTransportLocation der Alias des Adapters vorangestellt. Ein Adapter kann bei der Installation einen oder mehrere Aliase bei BizTalk Server registrieren. Die Engine analysiert den OutboundTransportLocation zur Laufzeit, um eine Übereinstimmung zu finden. Der Adapter ist für die Verarbeitung von OutboundTransportLocation mit oder ohne den Diesem vorangestellten Alias verantwortlich. Die folgende Tabelle zeigt einige Beispiele registrierter Aliase für die im Lieferumfang enthaltenen BizTalk-Adapter.
Adapteralias | Adapter | OutboundTransportLocation (Beispiel) |
---|---|---|
HTTP:// | HTTP | http://www. MyCompany.com/bar |
HTTPS:// | HTTP | https://www. MyCompany.com/bar |
mailto: | SMTP | mailto:A.User@MyCompany.com |
FILE:// | DATEI | FILE://C:\MyCompany\%MessageID%.xml |