Oracle進階隊列介紹,oracle隊列
原始連結:http://www.oracle-developer.net/display.php?id=411
oracle進階隊列介紹
進階隊列Advanced Queuing(AQ)在oracle多個版本都可得到。他是oracle原生訊息軟體並且在每一個版本都在加強。
這篇文章提供了一個AQ的進階概覽。尤其是我們將看到如何啟動一個隊列並進行入列--出列操作,還有通過通知建立非同步出列。
注意AQ支援資料庫意外的訊息監聽(例如JMS訊息佇列)。本文僅涉及資料庫內部訊息通訊。
要求:
本例要求指定的角色和許可權(除了標準CREATE SESSION/TABLE/PROCEDURE/TYPE和資料表空間配額外)
1、AQ_ADMINISTRATOR_ROLE: 用於建立隊列表和隊列;
2、EXECUTE ON DBMS_AQ:用於通知案例中啟用PLSQL預存程序編譯
另外,需要入列/出列訊息的標準應用程式使用者要求AQ許可權通過DBMS_AQADM[GRANT|REVOKE]_QUEUE_PRIVILIEGE API提供。
以下例子可以運行在任何擁有以上許可權的使用者下。
1、建立並啟動一個隊列
AQ處理的訊息稱為"有效負荷"(payloads) 。訊息格式可以是使用者自訂對象或XMLType或ANYDATA。當我們建立一個隊列,需要告訴oracle
有效負荷的結構,所以我們先建立一個簡單物件類型。
CREATE TYPE demo_queue_payload_type AS OBJECT
( message VARCHAR2(4000) );
/
我們有效負荷類型包含一個屬性,而現實中可能更複雜。下面建立隊列表用於儲存隊列訊息直到永久出列。
BEGIN
DBMS_AQADM.CREATE_QUEUE_TABLE (
queue_table => 'demo_queue_table',
queue_payload_type => 'demo_queue_payload_type'
);
END;
/
接著建立隊列並啟動:
BEGIN
DBMS_AQADM.CREATE_QUEUE (
queue_name => 'demo_queue',
queue_table => 'demo_queue_table'
);
DBMS_AQADM.START_QUEUE (
queue_name => 'demo_queue'
);
END;
/
至此,我們已經建立了隊列有效負荷,隊列表和隊列。來看下有哪些相關對象:
SELECT object_name, object_type
FROM user_objects
WHERE object_name != 'DEMO_QUEUE_PAYLOAD_TYPE';
OBJECT_NAME OBJECT_TYPE
------------------------------ ---------------
DEMO_QUEUE_TABLE TABLE
SYS_C009392 INDEX
SYS_LOB0000060502C00030$$ LOB
AQ$_DEMO_QUEUE_TABLE_T INDEX
AQ$_DEMO_QUEUE_TABLE_I INDEX
AQ$_DEMO_QUEUE_TABLE_E QUEUE
AQ$DEMO_QUEUE_TABLE VIEW
DEMO_QUEUE QUEUE
我們看到一個隊列帶出了一系列自動產生對象,有些是被後面直接用到的。不過有趣的是,建立了第二個隊列。這就是所謂的異常隊列(exception
queue)。如果AQ無法從我們的隊列接收訊息,將記錄在該異常隊列中。
2、入列訊息(enqueuing messages)
我們已經準備好使用DBMS_AQ.ENQUEUE API去入列一個訊息。接下來的例子,我們使用ENQUEUE過程入列一個單條訊息。
DBMS_AQ有大範圍的記錄和數群組類型來支援其介面並使我們去修改其行為(我們將在下面的例子看到2個此類引用)。
DECLARE
r_enqueue_options DBMS_AQ.ENQUEUE_OPTIONS_T;
r_message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;
v_message_handle RAW(16);
o_payload demo_queue_payload_type;
BEGIN
o_payload := demo_queue_payload_type('Here is a message');
DBMS_AQ.ENQUEUE(
queue_name => 'demo_queue',
enqueue_options => r_enqueue_options,
message_properties => r_message_properties,
payload => o_payload,
msgid => v_message_handle
);
COMMIT;
END;
/
可以看到入列訊息很簡單。入列操作是一個基本的事務(就像往隊列表Insert),因此我們需要提交。
3、瀏覽訊息(browsing messages)
在我們出列訊息之前,我們將"瀏覽"隊列內容。首先我們可以查詢AQ$DEMO_QUEUE_TABLE視圖看到多少訊息已經入列。正如我們早些看到的,
該視圖是在前面DBMS_AQADM.CREATE_QUEUE建立隊列自動產生的。
SELECT COUNT(*)
FROM aq$demo_queue_table;
COUNT(*)
----------
1
和我們預期一樣,隊列中只有一條訊息。我們有2種方法可以瀏覽訊息內容:
1)直接查詢檢視:
SELECT user_data
FROM aq$demo_queue_table;
USER_DATA(MESSAGE)
------------------------------------------------------------
DEMO_QUEUE_PAYLOAD_TYPE('Here is a message')
2)我們可以使用DBMS_AQ.DEQUEUE API瀏覽。根據名字可以看出,該過程用於出列訊息。為了達到只瀏覽不刪除的目的,我們可以使用
DBMS_AQ.BROWSE修改出列屬性(預設是DBMS_AQ.REMOVE)。
DECLARE
r_dequeue_options DBMS_AQ.DEQUEUE_OPTIONS_T;
r_message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;
v_message_handle RAW(16);
o_payload demo_queue_payload_type;
BEGIN
r_dequeue_options.dequeue_mode := DBMS_AQ.BROWSE;
DBMS_AQ.DEQUEUE(
queue_name => 'demo_queue',
dequeue_options => r_dequeue_options,
message_properties => r_message_properties,
payload => o_payload,
msgid => v_message_handle
);
DBMS_OUTPUT.PUT_LINE(
'*** Browsed message is [' || o_payload.message || '] ***'
);
END;
/
*** Browsed message is [Here is a message] ***
重新查詢視圖可以確定訊息確實沒被移除:
SELECT user_data
FROM aq$demo_queue_table;
USER_DATA(MESSAGE)
------------------------------------------------------------
DEMO_QUEUE_PAYLOAD_TYPE('Here is a message')
4、出列訊息(dequeuing messages)
現在我們將實際出列訊息。該操作不要求在同一會話進行(記住入列是AQ基於表的提交事務)。像入列,出列也是一個事務(從隊列表
移除訊息)。
DECLARE
r_dequeue_options DBMS_AQ.DEQUEUE_OPTIONS_T;
r_message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;
v_message_handle RAW(16);
o_payload demo_queue_payload_type;
BEGIN
DBMS_AQ.DEQUEUE(
queue_name => 'demo_queue',
dequeue_options => r_dequeue_options,
message_properties => r_message_properties,
payload => o_payload,
msgid => v_message_handle
);
DBMS_OUTPUT.PUT_LINE(
'*** Dequeued message is [' || o_payload.message || '] ***'
);
COMMIT;
END;
/
*** Dequeued message is [Here is a message] ***
PL/SQL procedure successfully completed.
重新查詢視圖發現訊息確已出列:
SELECT COUNT(*)
FROM aq$demo_queue_table;
COUNT(*)
----------
0
5、通知(notification)
文章的剩餘部分,我們將看一下通過通知自動出列。通過這種方式無論訊息何時入列,Oracle都將通知一個代理執行一個註冊的PLSQL
"回調"(callback)過程(可選擇地,代理還可以通知一個郵箱地址或HTTP://地址)。
為了說明,我們將建立和註冊一個PLSQL過程以通過通知方式管理我們的出列。這個回調過程將出列訊息並寫到一個資料庫表,以類比
標準資料庫操作。
作為開始,我們清理之前建立的對象。
BEGIN
DBMS_AQADM.STOP_QUEUE(
queue_name => 'demo_queue'
);
DBMS_AQADM.DROP_QUEUE(
queue_name => 'demo_queue'
);
DBMS_AQADM.DROP_QUEUE_TABLE(
queue_table => 'demo_queue_table'
);
END;
/
現在我們重新建立隊列表以允許多個消費者(consumers)。一個消費者是一個出列訊息代理(agent)啟用多個消費者是自動通知實現的
前提條件。
BEGIN
DBMS_AQADM.CREATE_QUEUE_TABLE (
queue_table => 'demo_queue_table',
queue_payload_type => 'demo_queue_payload_type',
multiple_consumers => TRUE
);
END;
/
接著重新建立並啟動我們的隊列。
BEGIN
DBMS_AQADM.CREATE_QUEUE (
queue_name => 'demo_queue',
queue_table => 'demo_queue_table'
);
DBMS_AQADM.START_QUEUE (
queue_name => 'demo_queue'
);
END;
/
為了證明通知的非同步特點,我們將把出列訊息存在一個應用表中。
CREATE TABLE demo_queue_message_table
( message VARCHAR2(4000) );
現在我們有一個應用表,我們可以建立回調PL/SQL。這個過程將出列觸發了通知的入列訊息。程式參數必須命名並類型化。入列訊息將
包含入列時間戳記,這樣插入到應用表中我們將看到訊息入列和通知出列的非同步延遲。
CREATE PROCEDURE demo_queue_callback_procedure(
context RAW,
reginfo SYS.AQ$_REG_INFO,
descr SYS.AQ$_DESCRIPTOR,
payload RAW,
payloadl NUMBER
) AS
r_dequeue_options DBMS_AQ.DEQUEUE_OPTIONS_T;
r_message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;
v_message_handle RAW(16);
o_payload demo_queue_payload_type;
BEGIN
r_dequeue_options.msgid := descr.msg_id;
r_dequeue_options.consumer_name := descr.consumer_name;
DBMS_AQ.DEQUEUE(
queue_name => descr.queue_name,
dequeue_options => r_dequeue_options,
message_properties => r_message_properties,
payload => o_payload,
msgid => v_message_handle
);
INSERT INTO demo_queue_message_table ( message )
VALUES ( 'Message [' || o_payload.message || '] ' ||
'dequeued at [' || TO_CHAR( SYSTIMESTAMP,
'DD-MON-YYYY HH24:MI:SS.FF3' ) || ']' );
COMMIT;
END;
/
我們還未完成通知步驟。我們需要向隊列增加一個訂閱者(subsriber)並註冊訂閱者接到通知時的動作(例如將執行我們的回調過程)。
BEGIN
DBMS_AQADM.ADD_SUBSCRIBER (
queue_name => 'demo_queue',
subscriber => SYS.AQ$_AGENT(
'demo_queue_subscriber',
NULL,
NULL )
);
DBMS_AQ.REGISTER (
SYS.AQ$_REG_INFO_LIST(
SYS.AQ$_REG_INFO(
'DEMO_QUEUE:DEMO_QUEUE_SUBSCRIBER',
DBMS_AQ.NAMESPACE_AQ,
'plsql://DEMO_QUEUE_CALLBACK_PROCEDURE',
HEXTORAW('FF')
)
),
1
);
END;
/
現在我們可以通過入列訊息來測試。這個訊息將僅包含一個時間戳記以便我們對比入列和自動出列發生的時間差。
DECLARE
r_enqueue_options DBMS_AQ.ENQUEUE_OPTIONS_T;
r_message_properties DBMS_AQ.MESSAGE_PROPERTIES_T;
v_message_handle RAW(16);
o_payload demo_queue_payload_type;
BEGIN
o_payload := demo_queue_payload_type(
TO_CHAR(SYSTIMESTAMP, 'DD-MON-YYYY HH24:MI:SS.FF3' )
);
DBMS_AQ.ENQUEUE(
queue_name => 'demo_queue',
enqueue_options => r_enqueue_options,
message_properties => r_message_properties,
payload => o_payload,
msgid => v_message_handle
);
COMMIT;
END;
/
為了查看我們的小時是否自動出列,我們將檢查應用表(DEMO_QUEUE_MESSAGE_TABLE)。
SELECT message
FROM demo_queue_message_table;
MESSAGE
---------------------------------------------------------------------------
Message [21-JUL-2005 21:54:51.156] dequeued at [21-JUL-2005 21:54:56.015]
我們可以看到通過通知非同步出列大約在入列操作後5秒後發生。
6、進一步閱讀
我們已經在本文接觸了AQ的能力。AQ是一個覆蓋面巨大的應用,原超出本文的這點介紹。更多資訊請參考:
http://docs.oracle.com/cd/B10501_01/appdev.920/a96587/toc.htm
ASKTOM:
https://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:8760267539329
全部原始碼下載:源碼下載
-------------------------
Dylan Presents.