Colas Multiple Consumer
Que son las colas Multiple_Consumer
En el apartado anterior hemos creado y trabajado con colas Single_Consumer.
En este apartado vamos a trabajar con colas Multiple_Consumer.
Cuando hablamos de colas Single o Multiple Consumer, nos estamos refiriendo, siempre, a cuantos destinatarios distintos tiene un mensaje.
En el apartado anterior, hemos utilizado colas Single_Consumer y, aunque trabajábamos con dos sesiones, un mensaje se desencolaba una sola vez (salvo los ROLLBACKS, que devolvian el mensaje a la cola).
En el caso de colas Multiple Consumers:
- Cada mensaje se desencola una y solo una vez por cada destinatario.
- En caso de ROLLBACK, el mensaje se devuelve a la cola para aquel destinatario.
- Cada destinatario puede desencolar los mensajes de forma independiente de los otros destinatarios: el sistema lleva un control de que destinatarios has desencolado cada mensaje y cuantos faltan por desencolar.
Creación de la cola Multiple_consumers
La creación de la cola Multiple_consumers es similar a la cola anterior. Solo se debe especificar la opción Multiple_consumers en la creación de la queue table. Por tanto, una misma queue table no puede tener colas Multiple_consumer y Single_Consumer.
La creación de la queue table y de la cola quedaria asi:
-- Creacion de la queue table indicando multiple_consumers BEGIN dbms_aqadm.create_queue_table ( queue_table=> 'tm_factura', -- nombre de la Queue table queue_payload_type=> 'factura', -- Tipo de datos que soporta entre comillas multiple_consumers=> TRUE, -- si es multiple consumers message_grouping=> DBMS_AQADM.NONE -- si agrupa los mensajes por transacciones ); COMMIT; --> no olvidar el commit END; / -- Creacion de la cola BEGIN dbms_aqadm.create_queue( queue_name=> 'cm_factura', -- Nombre de la cola queue_table=> 'tm_factura', -- Queue table en la que se crea queue_type=> DBMS_AQADM.NORMAL_QUEUE -- Hay colas normales y de error ); COMMIT; END; / BEGIN dbms_aqadm.start_queue('cm_factura', TRUE, TRUE); -- permitir encolar y desencolar COMMIT; END; /
Primer intento de encolar
Para encolar en una cola Multiple Consumers, podemos utilizar la rutina creada para el test de las colas Single_Consumers:
SQL> exec encolar_mensaje(3,'cm_factura'); BEGIN encolar_mensaje(3,'cm_factura'); END; * ERROR en la linea 1: ORA-24033: no hay ningún receptor del mensage ORA-06512: en "SYS.DBMS_AQ", linea 169 ORA-06512: en "ESQUEMA.ENCOLA", linea 6 ORA-06512: en "ESQUEMA.ENCOLAR_MENSAJE", linea 16 ORA-06512: en linea 1
Para encolar en una cola Multiple Consumers, AQ debe conocer los receptores del mensaje.
Que son los Receptores y Subscriptores de mensajes
En la práctica, esto significa que, para encolar en una cola Multiple Consumers:
- Se deben especificar los receptores en el enqueue del mensage.
- O bien, se deben definir subscriptores de los mensajes antes de encolar los mensajes.
Los subscriptores de una cola son los destinatarios por defecto. En efecto: por defecto, un mensaje encolado a una cola Multiple Consumers va destinado a todos y cada uno de los subscriptores de la cola.
Si un mensaje va destinado a otros destinatarios distintos (incluido un subconjunto de los subscriptores), se debe especificar la lista de receptores en el momento de enviar el mensaje.
Creación de un subscriptor para la cola y test de encolar
Vamos a crear un subscriptor, de nombre subscriptor_1, para la cola cm_factura. De esta forma, podemos encolar sin especificar subscriptores. Para definir subscriptores, se utiliza la estructura agent.
El agente está definido como:
TYPE SYS.AQ$_AGENT IS OBJECT ( name VARCHAR2(30), address VARCHAR2(1024), protocol NUMBER DEFAULT 0);
BEGIN
dbms_aqadm.add_subscriber(
queue_name => 'cm_factura',
subscriber => sys.aq$_agent('subscriptor_1', NULL ,0)
);
commit;
END;
/
-- ahora podemos encolar sin especificar destinatarios
SQL> exec encolar_mensaje(3,'cm_factura');
SQL> COMMIT;En estos momentos, tenemos en la cola la factura num 3 destinada al subscriptor subscriptor_1.
Vamos a crear otro subscriptor, de nombre subscriptor_2, para la cola cm_factura.
BEGIN
dbms_aqadm.add_subscriber(
queue_name => 'cm_factura',
subscriber => sys.aq$_agent('subscriptor_2', NULL, 0)
);
commit;
END;
/
-- ahora podemos encolar sin especificar destinatarios
SQL> exec encolar_mensaje(4,'cm_factura');
SQL> COMMIT;En estos momentos, tenemos en la cola la factura num 4 destinada a los subscriptores subscriptor_1 y subscriptor_2.
Ver el contenido de las colas MultipleConsumer
Cuando se crean las queue tables y las colas Multiple Consumers, se cean una serie de tablas y vistas que podemos consultar (pero no modificar su contenido). Todas ellas se forman a partir del nombre de la queue table.
Vamos a ver algunas de ellas con su contenido actual (2 subscriptore y los mensajes encolados):
Subscriptores de una cola
Se pueden ver consultando la vista AQ$<queuetable>_S
SQL> select * from AQ$TM_FACTURA_S; QUEUE NAME ADDRESS PROTOCOL TRANSFORMATION ------------ -------------- -------- --------- -------------- CM_FACTURA SUBSCRIPTOR_1 0 CM_FACTURA SUBSCRIPTOR_2 0
Mensajes que hay en una cola
Se pueden ver los mensajes que hay en una cola consultando la vista AQ$<queuetable>
Vamos a ver algunos campos:
SQL> select queue, msg_id, to_char(enq_time,'yyyy-mm-dd hh24:mi:ss') enq_time, msg_state, enq_txn_id,
consumer_name
from aq$tm_factura;
QUEUE MSG_ID ENQ_TIME MSG_STATE ENQ_TXN_ID CONSUMER_NAME
----------- --------------------------------- -------------------- ---------- ----------- -------------
CM_FACTURA 73ECB6703FEE0382E040007F01017DFB 2009-09-19 23:26:28 READY 2.17.4623 SUBSCRIPTOR_1
CM_FACTURA 73ECB6703FEF0382E040007F01017DFB 2009-09-19 23:30:24 READY 3.15.4651 SUBSCRIPTOR_1
CM_FACTURA 73ECB6703FEF0382E040007F01017DFB 2009-09-19 23:30:24 READY 3.15.4651 SUBSCRIPTOR_2
Podemos ver el identificador interno del mensaje (MSG_ID), hora que se encolado, su estado (READY indica disponible para desencolar), la transacción y el subscriptor o Consumidor.
Los MSG_ID son siempre distintospara distintos mensajes, pero, en este caso, los MSG_ID se diferencian solo en un carácter E/F después de 73ECB6703FE
Podemos ver que el primer mensaje tiene un único subscriptor y el segundo tiene 2.
Ver el contenido de los mensajes
Se puede ver el contenido de los mensajes en el campo user_data de la tabla <queueyable>. El nombre del campo, user_data, tiene el mismo nombre en todas las queue tables.
Veamos el contenido de un registro:
SQL> select user_data
from tm_factura
where msgid = '73ECB6703FEE0382E040007F01017DFB';
USER_DATA(NUM_FACTURA, FECHA, OBSERVACIONES, CLIENTE, PRODUCTOS(CANTIDAD, DESCRIPCION, PRECIO))
-----------------------------------------------------------------------------------------------
FACTURA(3, '19-SEP-09', 'observaciones factura', 'cliente 24', LINEAS(LINEA(2, 'desc1', 25), LI
NEA(4, 'desc2', NULL), LINEA(1, 'desc3', 1))
Desde sqlplus, el contenido se ve en formato objeto y con sus constructores.
Es de destacar que, exceptuando sqlplus, hay pocos programas que nos permitan ver directamente el contenido de los mensajes de las colas.
Encolar especificando Receptores
Si no nos interesa que un cierto mensaje vaya destinado a todos los subscriptores, podemos utilizar el envio a Receptores concretos (o lista de Receptores).
Veamos un ejemplo:
declare
enq_opt dbms_aq.enqueue_options_t;
msg_opt dbms_aq.message_properties_t;
msg_id raw(16);
lista dbms_aq.aq$_recipient_list_t; --> lista de receptores
begin
-- llenamos la lista de receptores
lista(1) := sys.aq$_agent('subscriptor_1', NULL, 0);
lista(2) := sys.aq$_agent('otro_subscriptor', NULL, 0);
-- hay que asignar la lista a la variable msg_opt
msg_opt.recipient_list := lista;
-- finalmente, encolamos el mensaje
dbms_aq.enqueue (
'cm_factura',
enq_opt,
msg_opt,
factura(25,sysdate,'especificando receptores', NULL,NULL),
msg_id);
commit;
end;
/
Obsérvese:
- Hemos enviado a dos receptores: uno es subscriptor y el otro no
- Cuando se envia a una lista explícita de receptores, no tienen por que ser subscriptores
- Los subscriptores se utilizan como destinatarios implícitos y por defecto
- Los receptores son destinatarios explícitos
- Cada mensaje puede tener unos Receptores distintos o bien los subscriptores de defecto.
- Cuando se envia a una lista explícita de receptores, no tienen por que ser subscriptores
- En este caso, la variable lista, que es un array, no lo dimensionamos previaente y lo vamos llenando directamente
- Esto es debido a que dbms_aq.aq$_recipient_list_t está definido como un type PL/SQL (y no un Object Type SQL conmo es el Object Type factura)
TYPE SYS.AQ$_RECIPIENT_LIST_T IS TABLE OF SYS.AQ$_AGENT INDEX BY BINARY_INTEGER;
Desencolar en colas Multiple Consumer
Para desencolar en colas multiple consumer, se debe especificar el consumidor o subscriptor.
Veámosmo con un ejemplo:
SQL> set serveroutput on
SQL> declare
deq_opt dbms_aq.dequeue_options_t;
msg_opt dbms_aq.message_properties_t;
msg_id raw(16);
datos factura;
begin
deq_opt.consumer_name := 'otro_subscriptor'; --> indicar el subscriptor
dbms_aq.dequeue (
'cm_factura',
deq_opt,
msg_opt,
datos,
msg_id);
dbms_output.put_line(datos.num_factura || ' ' || datos.observaciones);
end;
/
25 especificando receptores
SQL> commit;
La única diferencia con las solas Single Consumer es que hay que especificar el subscriptor.
