Gestione delle transazioni
Ricevitori sottoposti a transazioni
La differenza principale tra ricevitori transazioni e ricevitori non transazionati è che i ricevitori transazionati creano e usano una transazione MSDTC esplicita per garantire l'atomicità tra l'origine dati e il database MessageBox BizTalk Server. In generale qualsiasi altro aspetto dell'adapter è analogo.
È necessario notare che un adapter di ricezione richiesta-risposta utilizza solo una transazione per inoltrare il messaggio di richiesta originale al motore di messaggistica. Una transazione differente è necessaria per la trasmissione della risposta inviata dal motore di messaggistica all'adapter. Ciò perché l'ambito della prima transazione è tra l'adapter e il database MessageBox. Il successivo messaggio di richiesta non viene inviato all'adapter dal motore di messaggistica fin quando non viene eseguito il commit della transazione relativa al messaggio di richiesta originale.
Nel diagramma di interazione degli oggetti che segue viene illustrata l'interazione tra l'adapter e il motore di messaggistica durante un invio transazionale di messaggi in ingresso. Nell'esempio ha luogo la sequenza di interazioni seguente:
L'adapter riceve un nuovo batch dal motore.
L'adapter crea una nuova transazione MSDTC.
L'adapter non esegue un'operazione di lettura distruttiva dall'origine dati integrata nella transazione.
L'adapter invia il messaggio.
L'adattatore chiama Done nel batch, passando la transazione MSDTC e il puntatore di callback BatchComplete . Il motore restituisce un'interfaccia IBTDTCCommitConfirm .
Il motore elabora il batch, chiama nuovamente l'adapter nell'implementazione batchComplete e trasmette lo stato dell'elaborazione dei messaggi all'adapter.
Se il batch ha avuto esito positivo, l'adapter esegue il commit della transazione e chiama l'API IBTDTCCommitConmitConfirm con un
true
commit di valore.
Trasmettitori sottoposti a transazione
Gli adapter sottoposti a transazione sono per molti aspetti simili agli adapter non sottoposti a transazione. La maggiore differenza sta nel fatto che l'adapter sottoposto a transazione invia i dati nel messaggio a una risorsa integrata in una transazione MSDTC.
Suggerimento per l'implementazione: Per gli invii transazionati, l'adapter deve utilizzare la stessa transazione MSDTC per scrivere i dati nella destinazione e per eliminarli tramite la chiamata al metodo IBTTransportBatch.DeleteMessage . Solo queste due operazioni devono essere sottoposte a transazione. Non è necessario eseguire transazioni per qualsiasi altra operazione, ad esempio IBTTransportBatch.Resubmit, IBTTransportBatch.MoveToNextTransport e IBTTransportBatch.MoveToSuspendQ . Ciò perché il motore utilizza in modo implicito una transazione e questi tipi di operazioni non devono necessariamente essere atomici in relazione alla destinazione.
Nel seguente diagramma di interazione oggetto vengono illustrate le interazioni tra l'adapter e il motore. Di seguito viene riportata la sequenza degli eventi:
Il motore riceve un nuovo batch dall'adapter.
Il motore aggiunge due messaggi al nuovo batch.
Il motore chiama Done nel batch, causando la pubblicazione del batch del batch nella coda di trasmissione interna che viene eseguita dal pool di thread.
L'adapter crea una nuova transazione MSDTC.
L'adapter trasmette il messaggio integrando la destinazione nella transazione MSDTC. Ad esempio, potrebbe essere necessario scrivere in un database di SQL Server.
Dopo la trasmissione, l'adapter riceve un nuovo batch dal motore.
L'adattatore chiama DeleteMessage per i messaggi trasmessi correttamente.
L'adattatore chiama Done sul batch passando la relativa transazione DTC. Il motore restituisce un'interfaccia IBTDTCCommitConfirm .
Il motore elabora il batch eliminando i messaggi dalla coda dell'applicazione.
Il motore richiama l'interfaccia IBTBatchCallback dell'adattatore con informazioni sull'esito positivo delle operazioni di eliminazione.
Se per il batch non si sono verificati errori l'adapter esegue il commit delle transazioni.
L'adattatore chiama IBTDTCCommitConfirm.DTCCommitConfirm per informare il motore che è stato eseguito correttamente il commit della transazione.
Adapter di sollecitazione-risposta sottoposti a transazione
A differenza delle ricezione bidirezionali, le trasmissioni bidirezionali possono essere effettuate utilizzando la stessa transazione DTC. Gli adattatori Transacted solicit-response devono usare lo stesso IBTTransportBatch per le operazioni SubmitResponseMessage e DeleteMessage . Questo batch deve utilizzare la stessa transazione MSDTC utilizzata per trasmettere e ricevere la coppia di messaggi sollecitazione-risposta. Ciò garantisce atomicità per lo scambio di messaggi sollecitazione-risposta.
Componenti dei servizi e BYOT
Per le API del motore di messaggistica è necessario che venga fornita una transazione MSDTC. Tuttavia, alcuni componenti .NET sono stati ideati per essere utilizzati come componenti serviti e per non consentire che per l'esecuzione venga eseguito il commit o che venga interrotta a livello di codice. Al contrario, viene eseguito automaticamente il commit della transazione dal runtime COM+ di tale piattaforma.
Per questi scenari, l'adapter deve utilizzare il gateway BYOT (Bring Your Own Transaction). Ciò consente all'adapter di creare una transazione MSDTC, creare un'istanza per il componente .NET che utilizza la transazione e consentire al componente di ereditare la transazione creata invece di crearne una propria. .NET Framework fornisce System.EnterpriseServices.BYOT a questo scopo. L'SDK BaseAdapter fornisce una classe helper , BYOTTransaction, a questo scopo.
Come evitare race condition
Quando si scrive un adattatore che crea un oggetto transazione e lo si passa a BizTalk Server, si accetta la responsabilità di scrivere codice che esegue le operazioni seguenti:
Risolve errori nei messaggi associati al batch.
Decide il risultato finale della transazione associata all'operazione batch.
L'adattatore deve informare BizTalk Server sul risultato finale della transazione per mantenere i dati di rilevamento interni. L'adattatore informa BizTalk Server del risultato chiamando DTCConfirmCommit. Se l'adapter non esegue questa attività, si verificherà una considerevole perdita di memoria.
Le due attività sopra elencate, di risoluzione degli errori e di decisione del risultato finale, sembrano alquanto semplici, ma in realtà si basano su informazioni fornite da thread diversi:
L'adattatore elabora gli errori in base alle informazioni passate da BizTalk Server al callback BatchComplete nell'adapter. Questa richiamata viene eseguita sul thread dell'adapter.
DTCConfirmCommit è un metodo nell'oggetto IBTDTCCommitConfirm . Un'istanza dell'oggetto IBTDTCCommitConfirm viene restituita dalla chiamata batch IBTTransportBatch::D one . Questa istanza si trova nello stesso thread della chiamata IBTTransportBatch::D one , diversa dal thread dell'adapter.
Per ogni chiamata eseguita dall'adattatore a IBTTransportBatch::D one è presente un callback corrispondente, BatchComplete, chiamato dal motore di messaggistica in un thread separato per segnalare il risultato dell'invio in batch. In BatchComplete l'adapter deve eseguire il commit o il rollback della transazione in base al fatto che il batch sia passato o non riuscito. In entrambi i casi, l'adapter deve quindi chiamare DTCConfirmCommit per segnalare lo stato della transazione al motore di messaggistica.
Esiste una possibile race condition perché l'implementazione dell'adapter di BatchComplete può presupporre che l'oggetto IBTDTCCommitConfirm restituito da IBTTransportBatch::D one sia sempre disponibile quando Viene eseguito BatchComplete . Tuttavia, BatchComplete può essere chiamato in un thread separato del motore di messaggistica, anche prima che IBTTransportBatch::D one restituisca. È possibile che quando l'adattatore tenta di accedere all'oggetto IBTDTCCommitConfirm come parte dell'implementazione di BatchComplete , si verifica una violazione di accesso.
Il problema viene risolto con un evento come riportato nell'esempio seguente. In questo caso, l'accesso al puntatore dell'interfaccia avviene tramite una proprietà che utilizza l'evento. Get attende sempre il completamento di set.
protected IBTDTCCommitConfirm CommitConfirm
{
set
{
this.commitConfirm = value;
this.commitConfirmEvent.Set();
}
get
{
this.commitConfirmEvent.WaitOne();
return this.commitConfirm;
}
}
protected IBTDTCCommitConfirm commitConfirm = null;
private ManualResetEvent commitConfirmEvent = new ManualResetEvent(false);
Assegnare ora il valore restituito da IBTTransportBatch::D one a questa proprietà e usarlo nella chiamata BatchComplete .