Compartir a través de


Patrones de intercambio para adaptadores de envío

Los adaptadores de envío son mensajes entregados desde el motor de mensajería de BizTalk cuya transmisión debe efectuarse a través de la red. Estos mensajes pueden enviarse mediante un patrón de intercambio de mensajes bidireccional o unidireccional. Un adaptador que controla este patrón de intercambio de mensajes bidireccional se conoce como adaptador de petición-respuesta.

Transacciones de bloqueo frente a transmisiones de no bloqueo

El motor de mensajería entrega mensajes al adaptador de envío mediante el método IBTTransmitter.TransmitMessage o el método IBTTransmitterBatch.TransmitMessage , dependiendo de si el adaptador es compatible con lotes. Ambas versiones del método tienen un valor de devolución booleano que indica el modo en el que el adaptador efectúa la transmisión del mensaje. Si el adaptador devuelve true, ha enviado completamente el mensaje antes de devolverlo. En este caso, el motor de mensajería elimina el mensaje de la base de datos de cuadro de mensajes en nombre del adaptador. Si el adaptador devuelve false, inició la transmisión de mensajes y se devolvió antes de que se complete la transmisión. En este caso, el adaptador no es solo responsable de la eliminación del mensaje de la cola de aplicación correspondiente, sino también del control de errores de transmisión que requiere volver a intentar la transmisión o suspensión del mensaje.

La devolución false del adaptador es una llamada sin bloqueo, lo que significa que el código de implementación de TransmitMessage no bloquea el subproceso de llamada del motor de mensajería. El adaptador tan solo agrega el mensaje a una cola en memoria, preparado para su transmisión y, seguidamente, efectúa su devolución. El adaptador debería tener su propio grupo de subprocesos para efectuar la entrega de la cola en memoria, la transmisión del mensaje y, a continuación, la notificación al motor de la salida de la transmisión.

Por lo general, los subprocesos del motor de mensajería están más enlazados a la CPU que los subprocesos que se utilizan para enviar datos a través de la red. La combinación de estos dos tipos de subprocesos tiene un impacto negativo en el rendimiento. Los envíos de no bloqueo permiten separar estos dos tipos de subprocesos y mejorar considerablemente el rendimiento con respecto a las llamadas de bloqueo.

En el siguiente diagrama, se muestra el grupo de subprocesos del adaptador que, posiblemente, las operaciones E/S tiendan a enlazar. El enlace del grupo de subprocesos del motor de mensajería de BizTalk Server está más relacionado con el procesamiento de la CPU. El sistema funciona de forma más eficaz al conservar dos grupos de subprocesos distintos sin mezclar el mismo tipo de subproceso.

Diagrama que muestra el grupo de subprocesos del adaptador que pueden tienden a estar enlazados por operaciones de E/S.

Sugerencia de rendimiento: Para obtener el mejor rendimiento, los adaptadores de envío deben ser compatibles con lotes y sin bloqueo. Cuando se cambia el carácter de bloqueo y la incompatibilidad con lotes del adaptador de archivo de BizTalk al carácter de no bloqueo y de compatibilidad con lotes, se obtiene una mejora del rendimiento en tres aspectos.

Sugerencia de solución de problemas: El bloqueo de las transmisións puede provocar una degradación del rendimiento de una instancia de host completa. Si el adaptador realiza un bloqueo excesivo en TransmitMessage , impedirá que los subprocesos del motor entreguen mensajes a otros adaptadores.

Envíos no compatibles con lotes

Los adaptadores que no son compatibles con lotes deben implementar IBTTransmitter como se detalla en Interfaces para un adaptador de envío asincrónico. Para cada mensaje que el adaptador necesita transmitir el motor de mensajería llama a IBTTransmitter.TransmitMessage. En el diagrama de interacción de objetos que se muestra a continuación, se ilustra el enfoque habitual para la transmisión de mensajes, que consta de los pasos siguientes:

  1. El motor entrega el mensaje al adaptador.

  2. El adaptador coloca el mensaje en una cola en memoria, preparado para su transmisión.

  3. Un subproceso del grupo de subprocesos del adaptador quita el mensaje de la cola, lee la configuración que le corresponde a éste y lo transmite a través de la red.

  4. El adaptador obtiene un nuevo lote del motor.

  5. El adaptador llama a DeleteMessage en el lote, pasando el mensaje que acaba de transmitir.

  6. El adaptador llama a Listo en el lote.

  7. El motor procesa el lote y elimina el mensaje de la cola de la aplicación.

  8. El motor llama al adaptador para notificarle el resultado de la operación DeleteMessage .

    Imagen que muestra el adaptador que elimina un único mensaje de la cola de aplicaciones.

    El diagrama de interacción de objetos anterior muestra la eliminación que lleva a cabo el adaptador de un solo mensaje de la cola de la aplicación. Idealmente, el adaptador procesa por lotes operaciones de mensaje, lo que se opone al funcionamiento con un solo mensaje cada vez.

Envíos compatibles con lotes

Los adaptadores que son compatibles con lotes deben implementar IBTBatchTransmitter e IBTTransmitterBatch como se detalla en Interfaces para adaptadores de envío. Cuando el motor tiene mensajes para que el adaptador se transmita, el motor obtiene un nuevo lote del adaptador llamando a IBTBatchTransmitter.GetBatch. El adaptador devuelve un nuevo objeto por lotes que implementa IBTTransmitterBatch. A continuación, el motor inicia el lote llamando a IBTTransmitterBatch.BeginBatch. La API dispone de un parámetro de salida que permite que el adaptador especifique el número máximo de mensajes que se aceptará en el lote. De forma opcional, el adaptador puede devolver una transacción de DTC. A continuación, el motor llama a IBTTransmitterBatch.TransmitMessage una vez para cada mensaje saliente que se va a agregar al lote. El número de veces que se efectúa esta llamada es superior a 0 pero inferior o igual al tamaño máximo del lote, tal y como lo indica el adaptador. Cuando se han agregado todos los mensajes al lote, el adaptador llama a IBTTransmitterBatch.Done. En este punto, por lo general, el adaptador coloca todos los mensajes del lote en la cola en memoria correspondiente. El adaptador transmite los mensajes a partir de uno o varios subprocesos de su propio grupo de subprocesos, como en el caso de adaptadores no compatibles con lotes. Después, el adaptador notifica al motor del resultado de la transmisión.

En el diagrama de interacción de objetos que se muestra a continuación, se ilustra la transmisión que efectúa un adaptador de envío por lotes de dos mensajes.

Diagrama que muestra la transmisión de dos mensajes por un adaptador de envío por lotes.

Controlar errores de transmisión

En la ilustración siguiente, se ilustra la semántica recomendada para los errores de transmisión. Se trata únicamente de recomendaciones que no impone el motor de mensajería. Se puede desarrollar un adaptador que difiera de estas directrices si hay razones válidas para ello, aunque se debe tener especial cuidado en ese caso. Por ejemplo, en términos generales, un adaptador siempre debe mover mensajes al transporte de reserva una vez agotados todos los reintentos.

Lo más habitual es que un transporte necesite efectuar un número de reintentos superior al que establece la configuración. Aunque esta situación es ligeramente distinta, se considera aceptable porque se aumenta la capacidad de recuperación de la capa de transporte. En general, las API que expone el motor de mensajería están diseñadas para ofrecer al adaptador el control máximo allí donde sea posible. Este control conlleva un nivel más alto de responsabilidad.

Diagrama que muestra el proceso para controlar los errores de transmisión.

El adaptador determina el número de reintentos disponibles en un mensaje comprobando la propiedad de contexto del sistema RetryCount. El adaptador llama a la API Resubmit una vez para cada intento de reintento y pasa el mensaje que se va a volver a enviar. Además del mensaje, pasa la marca de tiempo que indica el momento en el que debe volver a entregarse el mensaje al adaptador. Este valor suele ser la hora actual más el valor de RetryInterval. RetryInterval es una propiedad de contexto del sistema cuyas unidades son minutos. Tanto RetryCount como RetryInterval en el contexto del mensaje son los valores configurados en el puerto de envío. Consideremos una implementación escalada horizontalmente con instancias del mismo host de BizTalk implementadas en varios equipos. Si el mensaje se entrega una vez agotado el intervalo de reintentos, podrá entregarse a cualquiera de las instancias de host de cualquiera de los equipos en los que aquéllas están configuradas para ejecutarse. Por ello, el adaptador no debería conservar ningún estado asociado con un mensaje que se deba utilizar en el reintento, ya que no hay garantía de que la misma instancia del adaptador se haga responsable, posteriormente, de la transmisión.

El adaptador solo debe intentar mover el mensaje al transporte de reserva después de que el recuento de reintentos sea inferior o igual a cero. Si no hay ningún transporte de reserva configurado para el puerto, el intento de mover el mensaje a dicho transporte no se llevará a cabo correctamente. En este caso, el mensaje debería suspenderse.

El código siguiente muestra cómo determinar el intervalo y el recuento de reintentos a partir del contexto del mensaje, así como el reenvío o el movimiento siguiente al transporte de reserva.

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;  
}  

Lanzar una excepción desde TransmitMessage

Si el adaptador produce una excepción en cualquiera de las API IBTTransmitter.TransmitMessage, IBTTransmitterBatch.TransmitMessage, IBTTransmitterBatch.Done, el motor trata la transmisión de los mensajes implicados como errores de transmisión y realiza la acción adecuada para el mensaje, tal como se detalla en How to Handle Adapter Failures.

En el caso de los adaptadores de envío compatibles con lotes, el lanzamiento de una excepción en la API TransmitMessage tiene como resultado la eliminación de la totalidad del lote y la puesta en práctica de las acciones de errores de transmisión predeterminadas para todos los mensajes de ese lote.

Petición-Respuesta

Los adaptadores de envío bidireccionales admiten, por lo general, tanto transmisiones bidireccionales como unidireccionales. El adaptador de envío determina si el mensaje se debe transmitir como un envío unidireccional o bidireccional inspeccionando la propiedad de contexto del sistema IsSolicitResponse en el contexto del mensaje.

En el siguiente fragmento de código se muestra este caso:

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 transmisión de petición-respuesta, el adaptador transmite el mensaje de petición. Una vez completado lo anterior, envía la respuesta asociada y le ordena al motor de mensajería que elimine el mensaje de petición original de la base de datos de cuadro de mensajes. La acción de eliminar el mensaje de la cola de la aplicación debe efectuarse en el mismo lote de proxy de transporte que el envío del mensaje de respuesta. Con ello, se asegura el carácter atómico de la eliminación y el envío de la respuesta. Para obtener una atomicidad completa, el adaptador debe utilizar una transacción de DTC en cuyo contexto se encuentren el mensaje de petición a un recurso compatible con transacciones, el envío del mensaje de respuesta y la eliminación del mensaje de petición. Como siempre, se recomienda que el mensaje de petición se transmita mediante un envío de no bloqueo.

En el siguiente fragmento de código se muestran los aspectos principales de un envío bidireccional. Cuando el motor llama a IBTTransmitter.TransmitMessage , el adaptador pone en cola el mensaje que se va a transmitir a una cola en memoria. El adaptador devuelve false para indicar que está realizando un envío sin bloqueo. El grupo de subprocesos del adaptador (WorkerThreadThunk) atiende la cola en memoria y pone en cola un mensaje para entregarlo a un método auxiliar. Este método es el responsable del envío del mensaje de petición y de la recepción del mensaje de respuesta. (La implementación de este método auxiliar está fuera del ámbito de este tema). El mensaje de respuesta se envía al motor y el mensaje de solicitud se elimina de la cola de la aplicación.

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;  
}  

Envíos dinámicos

Los puertos de envío dinámico no tienen una configuración de adaptador asociada a ellos. En lugar de ello, utilizan la configuración del controlador para cualquier propiedad predeterminada que necesite el adaptador para transmitir mensajes en un puerto dinámico. Por ejemplo, es posible que un adaptador de HTTP necesite utilizar un proxy y tenga que proporcionar credenciales. El nombre de usuario, la contraseña y el puerto pueden especificarse en la configuración del controlador que el adaptador guarda en la caché en tiempo de ejecución.

Para que el motor determine el transporte sobre el que se va a enviar un mensaje dinámico, outboundTransportLocation tiene el prefijo alias del adaptador. Un adaptador puede registrar uno o varios alias con BizTalk Server en el momento de la instalación. El motor analiza OutboundTransportLocation en tiempo de ejecución para buscar una coincidencia. El adaptador es responsable de controlar OutboundTransportLocation con o sin el alias antepuesto. En la tabla siguiente se muestran algunos ejemplos de alias registrados para adaptadores de BizTalk predeterminados.

Alias de adaptador Adapter (Adaptador) Ejemplo de 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