Oracle Advance Queuing是否適合您?
最近在EmbedIT工作中,我需要評估Oracle AQ是否是一個替代舊的非同步任務管理系統的不錯選擇。所以,讓我分享一下我的經驗。首先,有關Oracle AQ的文件非常龐大,因此我將指出您想知道的最重要的內容。
何時考慮Oracle AQ?
如果出於某種原因你不想搞像Hazelcast或Terracotta這樣的大資料框架,或者 透過JMS或AMPQ當前的訊息傳遞解決方案對你來說還不夠,那麼一定要檢查:Oracle Advance Queuing
Oracle帶來了自己的訊息傳遞,它透過資料庫工作。什麼時候適合你?
- 你每天都有數百萬封郵件。
- 你收到了很多訊息。
- 您希望以訊息方式與其他資料庫/應用程式通訊。
- Oracle AQ對分散式事務來說非常棒。甲骨文明確指出:
Oracle AQ的工作方式是不會將訊息視為dequeued 已出列(並因此被刪除,假設您在預設的破壞模式下出列) ,直到保留到所有消費者都已將該訊息取出為止。
如果處理某些事件涉及多個系統,並且主應用程式需要某種機制來知道其中一個處理失敗,則Oracle AQ是一個完美的候選者。
Oracle AQ的技術觀點
Oracle AQ允許將訊息排入佇列並從資料庫管理的佇列中出列,這裡每個佇列與一個佇列表相關聯。
每個佇列都有一個有效載荷,可以是:
- RAW
- OBJECT:指定型別的訊息。
- ANYDATA:具有任何物件型別的訊息。
Oracle AQ中的佇列可以是:
- 單個消費者佇列(只有一個消費者能夠在一瞬間出隊)
- 多個消費者佇列。可以透過以下方式實現:
1)多個收件人 - 在排隊之前設定郵件的收件人。2)多個訂戶 - 佇列具有預設的訂戶集。
如何使用Oracle AQ佇列
- 透過JMS API,只需將WebLogic配置為透過外部伺服器公開Oracle AQ佇列。
- 透過JDBC API(我將在演示中顯示)
- 透過C,.NET和許多其他語言。
重要說明:
Oracle AQ在訂閱者之間沒有任何型別的自動負載平衡,就像WebLogic JMS伺服器中存在迴圈一樣。但是你可以實現它。這並不難。
Oracle AQ演示
在演示的所有部分中,我將使用JDBC API和附加類AQTestObjectStruct和AQTestClient。
如何建立Oracle AQ表(多個消費者)
假設我們將在資料庫模式ho_kloucek_in中排隊“ Message_typ ” 型別的訊息。所以在資料庫中執行:
create or replace type ho_kloucek_in.Message_typ as object ( subject VARCHAR2(30), text VARCHAR2(80)) |
好的,我們有一個物件型別。現在我們可以為它建立帶佇列的佇列表。
public void createQueue() throws SQLException, AQException, ClassNotFoundException { AQQueueTableProperty qtable_prop; AQQueueProperty queue_prop; AQQueueTable q_table; AQQueue queue; java.sql.Connection aqconn = getOracleDataSource().getConnection(); aqconn.setAutoCommit(false); AQSession aqsession = null; // Register the Oracle AQ Driver Class.forName("oracle.AQ.AQOracleDriver"); try { AQEnqueueOption enqueueOption = new AQEnqueueOption(); aqsession = AQDriverManager.createAQSession(aqconn); qtable_prop = new AQQueueTableProperty("ho_kloucek_in.Message_typ"); qtable_prop.setMultiConsumer(true); /* Creating a queue table called aq_table1 in aqjava schema: */ q_table = aqsession.createQueueTable(queueOwner, queueTable, qtable_prop); System.out.println("Successfully created "+queueTable+" in "+queueOwner+" schema"); /* Creating a new AQQueueProperty object */ queue_prop = new AQQueueProperty(); /* Creating a queue called aq_queue1 in aq_table1: */ queue = aqsession.createQueue(q_table, queueName, queue_prop); queue.start(true, true); System.out.println("Successfully created "+queueName+" in "+queueOwner+""); } catch (Exception ex) { ex.printStackTrace(); } finally { aqsession.close(); aqconn.close(); } } |
請注意我是如何呼叫qtable_prop.setMultiConsumer(true)的
透過設定收件人列表來定位多個消費者
現在,在佇列表和佇列設定之後,讓我們用subsName變數設定名稱,將消費者作為收件人:
public void dequeueMessage(final String subsName) throws AQException, SQLException, ClassNotFoundException { java.sql.Connection aqconn = getOracleDataSource().getConnection(); aqconn.setAutoCommit(false); AQSession aq_sess = null; Class.forName("oracle.AQ.AQOracleDriver"); try { aq_sess = AQDriverManager.createAQSession(aqconn); AQQueue queue; AQMessage message; AQDequeueOption deq_option; queue = aq_sess.getQueue(queueOwner, queueName); AQDequeueOption opt = new AQDequeueOption(); opt.setConsumerName(subsName); while (true) { System.out.println("Waiting on subscription:"+subsName); message = queue.dequeue(opt, oracle.sql.STRUCT.class); if (message == null) { System.out.println("no messages"); } else { System.out.println("Successful dequeue"); if (message.getObjectPayload().getPayloadData() instanceof STRUCT) { STRUCT popedStruct = (STRUCT) message.getObjectPayload().getPayloadData(); System.out.println("subject: " + popedStruct.getAttributes()[0]); System.out.println("text: " + popedStruct.getAttributes()[1]); } //Commit aqconn.commit(); } } } finally { aq_sess.close(); aqconn.close(); } } |
在設定件收件人列表時,我們在排隊前設定收件人:
public void enqueueMessage(String xmlMessage) throws SQLException, AQException, ClassNotFoundException { java.sql.Connection aqconn = getOracleDataSource().getConnection(); aqconn.setAutoCommit(false); AQSession aqsession = null; // Register the Oracle AQ Driver Class.forName("oracle.AQ.AQOracleDriver"); try { AQEnqueueOption enqueueOption = new AQEnqueueOption(); aqsession = AQDriverManager.createAQSession(aqconn); AQQueue queue = aqsession.getQueue(queueOwner, queueName); AQMessage msg = queue.createMessage(); AQMessageProperty msgProps = new AQMessageProperty(); msgProps.setPriority(1); Vector recipientList = new Vector(); AQAgent subs1 = new AQAgent("Sub2", null, 0); recipientList.add(subs1); msgProps.setRecipientList(recipientList); msg.setMessageProperty(msgProps); AQObjectPayload payload = msg.getObjectPayload(); Object [] test_attributes = new Object[2]; test_attributes [0] = "AsyncTask"; test_attributes [1] = "121212666"; StructDescriptor personDesc = StructDescriptor.createDescriptor("HO_KLOUCEK_IN.MESSAGE_TYP", aqconn); STRUCT new_async = new STRUCT(personDesc, aqconn, test_attributes); payload.setPayloadData(new_async); queue.enqueue(enqueueOption, msg); aqconn.commit(); System.out.println("Message succesfully enqueued.."); } catch (Exception ex) { ex.printStackTrace(); } finally { aqsession.close(); aqconn.close(); } } |
此方法將訊息傳送到我的佇列,並且僅為名為“Sub2”的消費者者使用!
我們來試試吧。
啟動兩個JVM,使用引數“Sub1”和“Sub2”啟動前面提到的dequeueMessage方法... 使用包含dequeueMessage方法的類AQTestObjectStruct。以下內容應出現在兩個JVM中:
JVM1:
Waiting on subscription: Sub1
JVM2:
Waiting on subscription: Sub2
如您所見,預設情況下AQQueue.dequeue方法是阻塞的。無論如何,您也可以指定阻塞一段時間,請參閱文件和AQDequeueOption
現在啟動類AQTestClient和先前修改過的方法enqueueMessage傳送訊息
Object [] test_attributes = new Object[2]; test_attributes [0] = "AsyncTask"; test_attributes [1] = "11111"; |
對於訂戶“Sub2”:
AQMessage msg = queue.createMessage(); AQMessageProperty msgProps = new AQMessageProperty(); msgProps.setPriority(1); Vector recipientList = new Vector(); AQAgent subs2 = new AQAgent("Sub2", null, 0); recipientList.add(subs2); msgProps.setRecipientList(recipientList); msg.setMessageProperty(msgProps); |
consumer由AQAgent的名稱設定。現在,在啟動AQTestClient之後,應該在JVM2中出現:
Successful dequeue subject: AsyncTask text: 11111 Waiting on subscription: Sub2 |
訂閱的多個消費者
使用AQMessage的收件人列表引數,您將在獲取之前設定收件人。Oracle AQ文件明確指出:
如果在入隊期間指定了收件人列表,則它將覆蓋訂閱列表。
那麼讓我們看看如何建立佇列訂閱 ...讓我們更改AQTestObjectStruct中的dequeue方法並將其作為訂閱者啟動:
public void dequeueMessage(final String subsName) throws AQException, SQLException, ClassNotFoundException { java.sql.Connection aqconn = getOracleDataSource().getConnection(); aqconn.setAutoCommit(false); AQSession aq_sess = null; Class.forName("oracle.AQ.AQOracleDriver"); try { aq_sess = AQDriverManager.createAQSession(aqconn); AQQueue queue; AQMessage message; AQDequeueOption deq_option; queue = aq_sess.getQueue(queueOwner, queueName); // add subscription AQAgent subs = new AQAgent(subsName, null, 0); queue.removeSubscriber(subs); queue.addSubscriber(subs,null); AQDequeueOption opt = new AQDequeueOption(); opt.setConsumerName(subsName); while (true) { System.out.println("Waiting on subscription:"+subsName); message = queue.dequeue(opt, oracle.sql.STRUCT.class); if (message == null) { System.out.println("no messages"); } else { System.out.println("Successful dequeue"); if (message.getObjectPayload().getPayloadData() instanceof STRUCT) { STRUCT popedStruct = (STRUCT) message.getObjectPayload().getPayloadData(); System.out.println("subject: " + popedStruct.getAttributes()[0]); System.out.println("text: " + popedStruct.getAttributes()[1]); } //Commit aqconn.commit(); } } } finally { aq_sess.close(); aqconn.close(); } } |
(注意:如果要新增新訂閱者,請註釋掉“queue.removeSubscriber(subs)”行)
現在,您可以在排隊之前省略訊息中的收件人列表,因為訂閱會設定一組訊息目標。
讓我再引用Oracle AQ DOC:
如果enqueue的訊息生成者提供消費者的收件人列表,則無需為多消費者佇列指定訂閱。 在某些情況下,可能需要將針對特定消費者集的訊息排隊,而不是預設的訂戶列表。
這就是它!如果未在訊息中指定收件人列表,系統將向所有訂戶傳送訊息。如果指定收件人列表,則系統會將郵件傳遞給指定的收件人。如果你不指定收件人列表,佇列將沒有任何訂閱者(AQQueue.addSubscriber方法),那麼你最終會得到錯誤:
oracle.AQ.AQOracleSQLException: ORA-24033: no recipients for message ORA-06512: at "SYS.DBMS_AQIN", line 345 ORA-06512: at line 1 at oracle.AQ.AQOracleQueue.enqueue(AQOracleQueue.java:1267) at com.sachinhandiekar.oracle.aq.AQTestClient.enqueueMessage(AQTestClient.java:55) at com.sachinhandiekar.oracle.aq.AQTestClient.main(AQTestClient.java:84) |
測試多個訂閱者
首先改變的方法入隊中AQTestClient,就像我說的,我們可以努力忽略任何消費者的設定:
public void enqueueMessage(String xmlMessage) throws SQLException, AQException, ClassNotFoundException { java.sql.Connection aqconn = getOracleDataSource().getConnection(); aqconn.setAutoCommit(false); AQSession aqsession = null; // Register the Oracle AQ Driver Class.forName("oracle.AQ.AQOracleDriver"); try { AQEnqueueOption enqueueOption = new AQEnqueueOption(); aqsession = AQDriverManager.createAQSession(aqconn); AQQueue queue = aqsession.getQueue(queueOwner, queueName); AQMessage msg = queue.createMessage(); AQMessageProperty msgProps = new AQMessageProperty(); msgProps.setPriority(1); AQObjectPayload payload = msg.getObjectPayload(); Object [] test_attributes = new Object[2]; test_attributes [0] = "AsyncTask"; test_attributes [1] = "5555"; StructDescriptor personDesc = StructDescriptor.createDescriptor("HO_KLOUCEK_IN.MESSAGE_TYP", aqconn); STRUCT new_async = new STRUCT(personDesc, aqconn, test_attributes); payload.setPayloadData(new_async); queue.enqueue(enqueueOption, msg); aqconn.commit(); System.out.println("Message succesfully enqueued.."); } catch (Exception ex) { ex.printStackTrace(); } finally { aqsession.close(); aqconn.close(); } } |
現在透過兩個JVM中的AQTestObjectStruct類啟動前面提到的dequeueMessage方法和訂閱:
JVM1:Waiting on subscription: Sub1
JVM2:Waiting on subscription: Sub2
現在執行修改後的沒有收件人的AQTestClient.enqueueMessage方法和兩個JVM中的輸出將是:
**JVM1**: Waiting on subscription: Sub1 Successful dequeue subject: AsyncTask text: 5555 Waiting on subscription: Sub1 **JVM2**: Waiting on subscription: Sub2 Successful dequeue subject: AsyncTask text: 5555 Waiting on subscription: Sub2 |
如您所見,訊息已傳送給所有訂閱者,因為我們未在訊息中指定收件人。
總結
我希望我能很好地解釋所有細節。我真的很想念Oracle AQ的一些訊息預設負載均衡。這也許是我不會將它用於我們的應用程式節點之間的非同步任務分配的原因,因為我需要迴圈,WebLogic JMS免費提供給我。但是如果你想在某種分散式事務中與多個應用程式通訊,那麼肯定會使用Oracle AQ。
點選標題見原文,原始碼
相關文章
- 協作機器人是否適合您的應用?機器人
- 適合您的崗位資訊一覽
- 如何合併視訊?是否適合新手操作?
- 您適合從事web前端開發嗎?Web前端
- 你是否真的適合搞NDK開發?
- 怎麼判斷Linux是否適合你?Linux
- TDD 模式是否適合中小型企業?模式
- 如何選擇最適合您的代理提供商?
- Oracle11g active dataguard 是否適合於大資料量操作的系統Oracle大資料
- 如何確定敏捷是否適合你的團隊?敏捷
- 大資料架構和模式(二)——如何知道一個大資料解決方案是否適合您的組織大資料架構模式
- Docker是否比虛擬機器更適合我們?Docker虛擬機
- 我是否適合學習軟體開發技術?
- Apache Vs Nginx哪個最適合您的 Web 伺服器?ApacheNginxWeb伺服器
- 怎麼判斷自己是否適合學習六西格瑪?
- 海外動態IP和海外靜態IP:哪種更適合您?
- 如何為您選擇合適的製造執行系統(MES)?
- MariaDB快速選擇最適合您的需要的儲存引擎儲存引擎
- 恆訊科技分析:香港伺服器是否適合搭建app嗎?伺服器APP
- .選用適合的ORACLE優化器Oracle優化
- 什麼人適合學習Python?Python是否值得學習?Python
- 如何判斷豐田生產系統是否適合自己的企業?
- HDU2604QueuingUI
- F - Earn to Advance
- GCD-2 Queuing Tasks for DispatchGCUI
- 程式設計中尋找成就感:哪個程式設計崗位適合您?程式設計
- Postgres是否合適替代Redis或Kafka實現釋出訂閱作業? - HNRedisKafka
- Oracle意外發現PDB適合微服務和中臺Oracle微服務
- 哪個 Linux 發行版本最適合跑 OracleLinuxOracle
- 要換Apple Silicon Mac?M1 Mac可能不適合您的6個原因APPMac
- 究竟你適不適合買Mac?Mac
- 哪些人適合學python?python適合人群Python
- 工作效率就是合適的人做合適的事(轉)
- 人工智慧是否適合使用?將來我們能代替體力勞動嗎?人工智慧
- Python適合不適合零基礎學習呢?Python
- 現在加入Web前端學習還有市場嗎?自己是否適合學習前端Web前端
- 企業是否適合雲端計算,又如何進行雲端計算戰略規劃?
- 【等待事件】standby query scn advance事件