Procesar mensajes por lotes para el procesamiento de recepción
Devoluciones de llamada por lotes
Los lotes que los adaptadores de recepción envían al motor de mensajería se procesan de forma asíncrona. En consecuencia, los adaptadores necesitan un mecanismo para asociar una devolución de llamada a algún estado de adaptador con el fin de que se les pueda notificar sobre las acciones concluidas correctamente o con errores, así como llevar a cabo cualquier acción de limpieza que sea necesaria. La semántica de devolución de llamada es flexible para que los adaptadores puedan utilizar uno de los tres enfoques siguientes o una combinación de éstos. Son las siguientes:
Todas las devoluciones de llamada se realizan en la misma instancia de objeto que implementa IBTBatchCallBack.
La cookie pasada al motor al solicitar un nuevo lote se utiliza para correlacionar la devolución de llamada con el estado de los adaptadores.
Hay un objeto de devolución de llamada diferente para cada lote que implementa IBTBatchCallBack. En este caso, cada objeto mantiene su propio estado privado.
Cuando se ha procesado el lote, se vuelve a llamar al adaptador en su implementación de IBTBatchCallBack.BatchComplete. El primer parámetro, el estado HRESULT, indica el estado general del lote. Si este valor es igual o superior a cero, el lote se ha procesado correctamente, en el sentido de que el motor tiene la propiedad de los datos y el adaptador puede eliminarlos de la red. Un estado negativo indica que se produjo un error en el lote: ninguna de las operaciones del lote se realizó correctamente y el adaptador es responsable de controlar el error.
Si se producen errores en el lote, el adaptador necesita saber qué elemento de qué operación causó esos errores. Por ejemplo, supongamos que el adaptador estaba leyendo 20 archivos del disco y enviarlos a BizTalk Server mediante un único lote. Si el décimo archivo estaba dañado, el adaptador tendría que suspender ese archivo y volver a enviar el resto de 19. Esta información está disponible para el adaptador en los parámetros segundo y tercer:
opCount
(recuento de operaciones) de tipo short yoperationStatus
, que es del tipo BTBatchOperationStatus[].
Nota
En un objeto de lote único, nunca debe enviar un mensaje más de una vez. Si se envía el mismo objeto de mensaje varias veces en el mismo lote, se producirán errores en el motor.
El recuento de operaciones indica cuántos tipos de operaciones estaban en el lote (el tamaño de la matriz BTBatchOperationStatus ). Cada uno de los elementos de la matriz de estados de operaciones corresponde a un tipo de operación dado. Mediante el uso de la matriz BTBatchOperationStatus , el adaptador puede determinar qué elemento de una operación determinada no se pudo realizar examinando la matriz BTBatchOperationStatus.MessageStatus para detectar un error negativo en los valores HRESULT. En el escenario anterior, el adaptador crea un nuevo lote con el envío de 19 mensajes y la suspensión de 1 mensaje.
El fragmento de código siguiente muestra cómo un adaptador efectúa una solicitud de un nuevo lote desde el motor a través del proxy de transporte y envía un mensaje único al motor mediante el enfoque de cookies. El motor de mensajería llama al método BatchComplete como devolución de llamada por lotes cuando ha terminado de procesar todo el lote de mensajes enviados.
using Microsoft.BizTalk.TransportProxy.Interop;
using Microsoft.BizTalk.Message.Interop;
public class MyAdapter :
IBTTransport,
IBTTransportConfig,
IBTTransportControl,
IPersistPropertyBag,
IBaseComponent,
IBTBatchCallBack
{
private IBTTransportProxy _tp;
public void BatchComplete(
Int32 status,
Int16 opCount,
BTBatchOperationStatus[] operationStatus,
System.Object callbackCookie)
{
// Use cookie to correlate callback with work done,
// in this example the batch is to submit a single
// file the name of which will be in the
// callbackCookie
string fileName = (string)callbackCookie;
if ( status >= 0 )
// DeleteFile from disc
File.Delete(fileName);
else
// Rename file to fileName.bad
File.Move(fileName, fileName + ".bad");
}
private void SubmitMessage(
IBaseMessage msg,
string fileName)
{
// Note: Pass in the filename as the cookie
IBTTransportBatch batch =
_tp.GetBatch(this, (object)fileName);
// Add msg to batch for submitting
batch.SubmitMessage(msg);
// Process this batch
batch.Done(null);
}
}
El SDK de BizTalk Server incluye ejemplos para los siguientes adaptadores: File, HTTP, MSMQ y transactional Adapter. Todos estos adaptadores se crean sobre un bloque de creación común denominado BaseAdapter. La versión 1.0.1 de BaseAdapter incluye todo el código relevante para analizar el estado de la operación y volver a crear un nuevo lote para su envío.
Condición de carrera
Las dos tareas de resolución de errores y decisión del resultado final de un lote enviado parecen bastante sencillas, pero en realidad se basan en información procedente de varios subprocesos:
El adaptador procesa errores en función de la información pasada por BizTalk Server al método de devolución de llamada BatchComplete del adaptador. Esta devolución de llamada se ejecuta en el subproceso del adaptador.
DTCCommitConfirm es un método en el objeto IBTDTCCommitConfirm . La llamada IBTTransportBatch::D one devuelve una instancia del objeto IBTDTCCommitConfirm. Esta instancia está en el mismo subproceso que la llamada IBTTransportBatch::D one , que es diferente del subproceso de devolución de llamada del adaptador.
Para cada llamada que realiza el adaptador a IBTTransportBatch::D one el motor de mensajería realiza una llamada correspondiente al método de devolución de llamada BatchComplete en un subproceso independiente para notificar el resultado del envío por lotes. En BatchComplete , el adaptador debe confirmar o revertir la transacción en función de si el lote se ha pasado o no. En cualquier caso, el adaptador debe llamar a DTCCommitConfirm para notificar el estado de la transacción.
Existe una posible condición de carrera porque la implementación del adaptador de BatchComplete puede suponer que el objeto IBTDTCCommitConfirm devuelto por IBTTransportBatch::D one siempre está disponible cuando se ejecuta BatchComplete . Sin embargo, se puede llamar a BatchComplete en un subproceso de motor de mensajería independiente, incluso antes de que IBTTransportBatch::D one devuelva. Es posible que cuando el adaptador intente acceder al objeto IBTDTCCommitConfirm como parte de la implementación de BatchComplete , hay una infracción de acceso porque ya no existe el subproceso que realiza la llamada. Para evitar este problema, ponga en práctica lo que se especifica a continuación.
En el siguiente ejemplo, el problema se soluciona con el uso de un evento. En este caso, se tiene acceso al puntero de la interfaz mediante una propiedad que utiliza el evento. get siempre espera a que set finalice para comenzar.
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);
Códigos de estado de lotes
El código de estado de lote general indica el resultado del lote. El estado de la operación ofrece el código de estado de envío de los elementos de mensajes individuales. El error de los lotes puede producirse por varias razones. Es posible que se produzca un error relacionado con la seguridad o que el mensaje se haya enviado cuando el motor se estaba cerrando. (Normalmente, cuando el motor se apaga, no acepta ningún trabajo nuevo, pero permite que se complete el trabajo en proceso). En la tabla siguiente se indican algunos valores HRESULT comunes que se devuelven en el estado del lote o en el estado de la operación. También se indica si se trata de códigos correctos o con errores. El control adecuado de estos códigos se describe más por completo en Cómo controlar los errores del adaptador.
Código (definido en la clase BTTransportProxy) | Código correcto o con errores | Descripción |
---|---|---|
BTS_S_EPM_SECURITY_CHECK_FAILED | Correcto | El puerto se configuró para realizar una comprobación de seguridad y eliminar los mensajes con errores de autenticación. Los adaptadores no deben suspender los lotes que devuelvan este código de estado. |
BTS_S_EPM_MESSAGE_SUSPENDED | Correcto | Indica la suspensión de uno o varios mensajes, además de que el motor tiene la propiedad de los datos. Se trata de un código correcto en el que el motor de mensajería ha aceptado el envío del mensaje. |
E_BTS_URL_DISALLOWED | Error | Se envió un mensaje con inboundTransportLocation no válido. |
Operaciones por lotes
En la tabla siguiente se detallan los métodos de miembro y las operaciones del objeto IBTTransportBatch que usa el adaptador para agregar trabajo a un lote determinado.
Nombre del método | Tipo de operación | Descripción |
---|---|---|
SubmitMessage | Enviar | Envía un mensaje al motor. |
SubmitResponseMessage | Enviar | Envía un mensaje de respuesta al motor. Se trata del mensaje de respuesta de un par petición-respuesta. |
DeleteMessage | Eliminar | Elimina un mensaje transmitido correctamente por un adaptador a través de la red mediante un envío de no bloqueo. Los adaptadores que usan envíos de bloqueo no necesitan llamar a este método porque el motor de mensajería lo eliminará en nombre del adaptador si el adaptador vuelve true de TransmitMessage. |
MoveToSuspendQ | MoveToSuspendQ | Mueve los mensajes a la cola de suspensión. |
Resubmit | Resubmit | Solicita que se vuelva a intentar transmitir un mensaje en un momento posterior. Por lo general, se llama cuando se han producido errores en un intento de transmisión. |
MoveToNextTransport | MoveToNextTransport | Solicita la transmisión de un mensaje mediante su transporte de copia de seguridad. Por lo general, se llama una vez agotados todos los reintentos. Si no hay ningún transporte de copia de seguridad, éste método no se ejecutará correctamente. |
SubmitRequestMessage | SubmitRequest | Envía un mensaje de solicitud. Se trata del mensaje de solicitud de un par solicitud-respuesta. |
CancelRequestForResponse | CancelRequestForResponse | Notifica al motor que el adaptador no desea esperar el mensaje de respuesta de un par solicitud-respuesta. |
Borrar | N/D | Borra todo el trabajo del lote. |
Listo | N/D | Envía un lote de mensajes al motor para su procesamiento. |
Administración de Batch
Los lotes se implementan en código nativo en el motor de mensajería. Por ello, un adaptador escrito en código administrado debe liberar el contenedor al que se puede llamar en tiempo de ejecución (RCW) para el lote después de finalizar con dicho lote. Esto se hace en código administrado mediante la API Marshal.ReleaseComObject . Es importante recordar que la llamada a esta API debe efectuarse en un bucle Mientras hasta que el recuento de referencia devuelto sea igual a cero.
BizTalk Server procesa los mensajes de forma sincrónica dentro de un lote. Es posible procesar muchos lotes a la vez, lo que permite optimizar el adaptador al ajustar el tamaño del lote en el dominio de la aplicación. Por ejemplo, se pueden procesar todos los archivos de un sitio FTP (hasta que se alcance cierto límite). En el caso de SAP, es posible procesar una única secuencia en forma de varios mensajes que posteriormente se envían como un lote.
El lote usado por un adaptador para volver a pasar las operaciones a BizTalk Server no necesita corresponder exactamente a la lista de mensajes que BizTalk Server proporciona al adaptador. En otras palabras, al realizar envíos transaccionales, debe dividir las operaciones de reenvío MoveToNextTransport y MoveToSuspendQ en lotes independientes. Muchos adaptadores ordenan un lote de mensajes enviados a varios extremos en listas independientes de mensajes para su procesamiento posterior.
El punto es que no hay reglas (además de las asociadas a la transacción) asociadas al procesamiento por lotes de mensajes en BizTalk Server. El procesamiento por lotes es simplemente una manera específica de la implementación para que el adaptador fragmenta los mensajes dentro y fuera de BizTalk Server.
Un adaptador puede procesar mensajes por lotes de varios lotes más pequeños que BizTalk Server lo han dado en un lote mayor para la respuesta a BizTalk Server. Con ello se puede lograr una optimización considerable de los adaptadores transaccionales que utilizan un elevado número de mensajes muy pequeños. Esto reduce al mínimo la proporción del número total de transacciones por mensaje.
Normalmente, BizTalk Server genera lotes de envío de entre 5 y 10 mensajes. Si son mensajes muy pequeños, un adaptador puede procesar por lotes hasta 100 mensajes o más antes de enviar un lote transaccional de eliminaciones a BizTalk Server. Una optimización de este tipo no es fácil de implementar; es preciso asegurarse de que los mensajes no se bloquean nunca en la memoria del adaptador como resultado de esperar interminablemente a que se alcance un determinado umbral.
La importancia del procesamiento por lotes se puede ver en los números de rendimiento de BizTalk Server para los adaptadores de alto rendimiento, como MQSeries. En estos adaptadores, los mensajes se reciben con al menos el doble de la frecuencia con la que se envían. De forma predeterminada, el adaptador de recepción usa lotes de 100 mensajes y el adaptador de envío usa el lote de BizTalk Server predeterminado que se proporciona.
Lotes transaccionales
Al escribir un adaptador que crea un objeto de transacción y lo entrega a BizTalk Server, acepta la responsabilidad de escribir código que haga lo siguiente:
Decide el resultado final de la operación por lotes: para confirmar o finalizar la transacción. Esto puede depender de otras ramas transaccionales dentro del ámbito de esta transacción que no son BizTalk Server dependientes, como escribir en una cola de Message Queuing (MSMQ) o en una operación de base de datos transaccional.
Informa BizTalk Server del resultado final llamando al método IBTDTCCommitConfirm.DTCCommitConfirm. Devolver
true
indica una confirmación correcta de la transacción; devolverfalse
significa un error y una reversión de la transacción.El adaptador debe informar BizTalk Server sobre el resultado final de la transacción para mantener sus datos de seguimiento internos. El adaptador informa BizTalk Server del resultado llamando a DTCCommitConfirm. Si el adaptador no hace esto, se produce una pérdida de memoria considerable y la transacción de Microsoft DTC puede superar el tiempo de espera y presentar errores a pesar de que las operaciones se hayan finalizado correctamente.