Modelli di scambio per gli adapter di trasmissione
Gli adapter di trasmissione sono messaggi inviati dal motore di messaggistica BizTalk per la trasmissione in rete. Tali messaggi possono essere inviati mediante un modello di scambio dei messaggi unidirezionale o bidirezionale. Gli adapter che gestiscono il modello di scambio dei messaggi bidirezionale sono definiti adapter di sollecitazione-risposta.
Trasmissioni bloccanti e non bloccanti
Il motore di messaggistica recapita messaggi all'adapter di invio usando il metodo IBTTransmitter.TransmitMessage o il metodo IBTTransmitterBatch.TransmitMessage, a seconda che l'adapter sia in grado di essere in batch. Entrambe le versioni del metodo restituiscono un valore booleano che indica il tipo di trasmissione utilizzato dall'adapter per il messaggio. Se l'adattatore restituisce true
, ha inviato completamente il messaggio prima di restituire. In questo caso il motore di messaggistica elimina il messaggio dal database MessageBox per conto dell'adapter. Se l'adattatore restituisce false
, ha avviato la trasmissione dei messaggi e restituita prima del completamento della trasmissione. In tal caso l'adapter è responsabile non solo dell'eliminazione del messaggio dalla relativa coda dell'applicazione ma anche della gestione degli errori di trasmissione che richiedono un nuovo tentativo di trasmissione o la sospensione del messaggio.
La restituzione dell'adapter è una chiamata non sbloccante, ovvero il codice di implementazione di TransmitMessage non blocca il thread chiamante false
del motore di messaggistica. L'adapter viene semplicemente restituito dopo aver aggiunto il messaggio a una coda in memoria pronta ad essere trasmessa. Per l'adapter è disponibile un pool di thread specifico preposto alla gestione della coda in memoria, alla trasmissione del messaggio e alla notifica del risultato della trasmissione al motore.
Di solito, i thread del motore di messaggistica tendono a essere più associati alla CPU rispetto a quelli utilizzati per trasmettere dati in rete. La combinazione di questi due tipi di thread incide negativamente sulle prestazioni. Le trasmissioni non bloccanti consentono di dissociare i due tipi di thread determinando un miglioramento significativo delle prestazioni rispetto alle chiamate bloccanti.
Nella figura che segue viene illustrato il pool di thread dell'adapter che può tendere a essere vincolato alle operazioni di I/O. Il pool di thread del motore di messaggistica di BizTalk Server è più vincolato all'elaborazione della CPU. Per aumentare l'efficacia del sistema, è possibile mantenere due pool di thread distinti ed evitare di combinare thread dello stesso tipo.
Suggerimento sulle prestazioni: Per ottenere prestazioni ottimali, gli adattatori di invio devono essere non in grado di sbloccare e tenere presente in batch. La sostituzione di un adapter FILE BizTalk bloccante non in grado di supportare batch con uno non bloccante in grado di supportare batch ha consentito di triplicare le prestazioni.
Suggerimento per la risoluzione dei problemi: Il blocco delle trasmissioni può causare una riduzione delle prestazioni di un'intera istanza host. Se l'adattatore esegue un blocco eccessivo in TransmitMessage , impedisce ai thread del motore di recapitare messaggi ad altri adattatori.
Trasmissioni non in batch
Gli adattatori che non sono in batch devono implementare IBTTransmitter come descritto in Dettaglio nelle interfacce per un adattatore di invio asincrono. Per ogni messaggio che l'adattatore deve trasmettere il motore di messaggistica chiama IBTTransmitter.TransmitMessage. Nel diagramma di interazione degli oggetti che segue viene illustrato in dettaglio il tipico approccio per la trasmissione dei messaggi, che si sviluppa nei passaggi seguenti:
Il motore invia il messaggio all'adapter.
L'adapter aggiunge il messaggio a una coda in memoria pronta ad essere trasmessa.
Un thread del pool di thread dell'adapter elimina il messaggio dalla coda, ne legge la configurazione e lo trasmette in rete.
L'adapter riceve un nuovo batch dal motore.
L'adapter chiama DeleteMessage nel batch, passando il messaggio appena trasmesso.
L'adattatore chiama Fine sul batch.
Il motore elabora il batch ed elimina il messaggio dalla coda dell'applicazione.
Il motore richiama l'adapter per notificarlo al risultato dell'operazione DeleteMessage .
Nel precedente diagramma di interazione degli oggetti viene illustrata l'eliminazione dalla coda dell'applicazione di un singolo messaggio a opera dell'adapter. L'adapter elabora in batch le operazioni sui messaggi, anziché elaborare i messaggi uno alla volta.
Trasmissioni in batch
Gli adattatori che sono in batch devono implementare IBTBatchTransmitter e IBTTransmitterBatch , come illustrato in Dettaglio nelle interfacce per gli adapter di trasmissione. Quando il motore contiene messaggi per la trasmissione dell'adattatore, il motore ottiene un nuovo batch dalla scheda chiamando IBTBatchTransmitter.GetBatch. L'adapter restituisce un nuovo oggetto batch che implementa IBTTransmitterBatch. Il motore avvia quindi il batch chiamando IBTTransmitterBatch.BeginBatch. Questa API è dotata di un parametro out che consente all'adapter di specificare il numero massimo di messaggi accettati nel batch. È possibile che l'adapter restituisca facoltativamente una transazione DTC. Il motore chiama quindi IBTTransmitterBatch.TransmitMessage una volta per ogni messaggio in uscita da aggiungere al batch. Il numero di volte in cui viene chiamato il metodo è maggiore di zero ma minore di o uguale alle dimensioni massime del batch indicate dall'adapter. Quando tutti i messaggi sono stati aggiunti al batch, l'adapter chiama IBTTransmitterBatch.Done. In questa fase, in genere, l'adapter elimina dalla relativa coda in memoria tutti i messaggi contenuti nel batch. L'adapter trasmette i messaggi da uno o più thread del pool di thread specifico analogamente agli adapter che non sono in grado di supportare batch. L'adapter notifica quindi al motore il risultato della trasmissione.
Nel diagramma di interazione degli oggetti che segue viene illustrata la trasmissione di due messaggi da parte di un adapter di trasmissioni in batch.
Gestione degli errori di trasmissione
Nella figura che segue viene illustrata la semantica consigliata per gli errori di trasmissione. Si tratta solo di suggerimenti che non vengono imposti dal motore di messaggistica. È possibile sviluppare un adapter che si discosti da queste linee guida se esistono motivi validi al riguardo, ma in tal caso è necessario prestare attenzione. In genere è preferibile che un adapter sposti sempre i messaggi nel trasporto di backup dopo aver esaurito tutti i tentativi.
La situazione più comune è che un trasporto richieda l'utilizzo di un numero di tentativi maggiore di quello configurato. Anche se leggermente diversa dal previsto, tale situazione può essere considerata accettabile in quanto determina un incremento della capacità del layer di trasporto. In genere le API esposte dal motore di messaggistica sono progettate per fornire il controllo massimo dell'adapter, laddove sia possibile, con conseguente aumento del livello di responsabilità.
L'adapter determina il numero di tentativi disponibili in un messaggio controllando la proprietà di contesto di sistema RetryCount. L'adapter chiama l'API Resubmit una volta per ogni tentativo di ripetizione e passa il messaggio da inviare nuovamente. Insieme al messaggio viene inviato il time stamp che indica quando il motore restituirà il messaggio all'adapter. Questo valore deve in genere essere l'ora corrente più il valore di RetryInterval. RetryInterval è una proprietà di contesto di sistema le cui unità sono minuti. Sia RetryCount che RetryInterval nel contesto del messaggio sono i valori configurati nella porta di invio. Si prenda in considerazione una distribuzione con scalabilità orizzontale con le istanze dello stesso host BizTalk distribuite in più computer. Se il messaggio viene inviato dopo l'esaurimento dell'intervallo di tentativi, può essere inviato a una qualsiasi delle istanze dell'host presenti su uno dei computer in cui ne sia stata configurata l'esecuzione. Per questo motivo, è consigliabile che l'adapter non gestisca eventuali stati associati a un messaggio da utilizzare durante il tentativo, in quanto non esiste alcuna certezza che la stessa istanza dell'adapter sia responsabile della trasmissione in un secondo momento.
L'adapter deve solo tentare di spostare il messaggio al trasporto di backup dopo che il numero di tentativi ha raggiunto un valore minore di o uguale a zero. Eventuali tentativi di spostamento del messaggio al trasporto di backup non riescono se per la porta non è stato configurato un trasporto di backup. In questo caso il messaggio deve essere sospeso.
Nel frammento di codice che segue vengono illustrati la modalità di definizione del numero e dell'intervallo di tentativi dal contesto del messaggio e il successivo reinvio o spostamento al trasporto di backup.
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;
}
Generazione di un'eccezione da TransmitMessage
Se l'adapter genera un'eccezione in una delle API IBTTransmitter.TransmissionMessage, IBTTransmitterBatch.TransmissionMessage, IBTTransmitterBatch.Done, il motore considera la trasmissione dei messaggi coinvolti come errori di trasmissione e esegue l'azione appropriata per il messaggio, come descritto in Come gestire gli errori dell'adattatore.
Per gli adapter di trasmissione in grado di supportare batch, la generazione di un'eccezione nell'API TransmitMessage determina l'eliminazione dell'intero batch e l'esecuzione delle azioni predefinite di correzione degli errori di trasmissione per tutti i messaggi del batch in oggetto.
Sollecitazione-risposta
Gli adapter di trasmissione bidirezionali, di solito, supportano sia le trasmissioni unidirezionali sia quelle bidirezionali. L'adapter di invio determina se il messaggio deve essere trasmesso come invio unidirezionale o bidirezionale controllando la proprietà contesto di sistema IsSolicitResponse nel contesto del messaggio.
Nel frammento di codice che segue viene illustrata questa alternativa:
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;
Durante una trasmissione sollecitazione-risposta l'adapter trasmette il messaggio di sollecitazione, quindi invia la risposta associata e richiede al motore di messaggistica di eliminare il messaggio di sollecitazione originale dal database MessageBox. L'azione di eliminazione del messaggio dalla coda dell'applicazione deve essere eseguita nello stesso batch del proxy di trasporto dell'invio del messaggio di risposta. In questo modo viene garantita l'atomicità dell'eliminazione e dell'invio della risposta. Per ottenere atomicità completa, l'adapter deve utilizzare una transazione DTC in modo che la trasmissione del messaggio di sollecitazione a una risorsa in grado di supportare transazioni, l'invio del messaggio di risposta e l'eliminazione del messaggio di sollecitazione vengano eseguiti tutti nel contesto della stessa transazione DTC. Come sempre, è consigliabile trasmettere il messaggio di sollecitazione mediante una trasmissione non bloccante.
Nel frammento di codice seguente vengono illustrati gli aspetti principali di una trasmissione bidirezionale: Quando il motore chiama IBTTransmitter.TransmitMessage , l'adapter esegue la sequenza del messaggio da trasmettere a una coda in memoria. L'adapter restituisce false
per indicare che sta eseguendo un invio senza blocco. Il pool di thread dell'adapter (WorkerThreadThunk) esegue il servizio della coda in memoria e dequeue un messaggio per consegnarlo a un metodo helper.The adapter's thread pool (WorkerThreadThunk) services the in-memory queue and dequeues a message to hand off to a helper method. Il metodo in questione è preposto all'invio del messaggio di sollecitazione e alla ricezione del messaggio di risposta. L'implementazione di questo metodo helper non rientra nell'ambito di questo argomento. Il messaggio di risposta viene inviato al motore e il messaggio di richiesta viene eliminato dalla coda dell'applicazione.
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;
}
Trasmissioni dinamiche
Alle porte di trasmissione dinamica non è associata la configurazione di un adapter. Tali porte utilizzano infatti la configurazione del gestore per le proprietà predefinite richieste dall'adapter per trasmettere messaggi in una porta dinamica. Per un adapter HTTP può ad esempio essere necessario utilizzare un proxy e fornire le credenziali. È possibile specificare il nome utente, la password e la porta nella configurazione del gestore memorizzata dall'adapter nella cache in fase di esecuzione.
Per determinare il trasporto che deve essere inviato un messaggio dinamico, outboundTransportLocation è preceduto dall'alias dell'adattatore. Un adattatore può registrare uno o più alias con BizTalk Server in fase di installazione. Il motore analizza outboundTransportLocation in fase di esecuzione per trovare una corrispondenza. L'adapter è responsabile della gestione di OutboundTransportLocation con o senza l'alias prependato. Nella tabella riportata di seguito vengono illustrati alcuni esempi di alias registrati per gli adapter BizTalk disponibili.
Alias adapter | Adattatore | Esempio di OutboundTransportLocation |
---|---|---|
HTTP:// | HTTP | http://www. MyCompany.com/bar |
HTTPS:// | HTTP | https://www. MyCompany.com/bar |
mailto: | SMTP | mailto:A.User@MyCompany.com |
FILE:// | FILE | FILE://C:\ MyCompany \%MessageID%.xml |