四、使用Events
Event直譯對應的中文解釋是指事件,不過單純講事件畢竟太抽象了,舉個樣本來形容吧。A(對應某個應用程式,或者是ORACLE中的進程)在幹活時突然眉頭一皺說道,不好,前方有情況,這可怎麼辦。這時,只見它認真想了想,過了一會兒臉上一喜說道:有了,俗話說早請示啊晚彙報,出現情況要找領導,趕緊給領導發訊息唄。於是B(也是對應某個應用或ORACLE進程)就收到了一條A發過來的"前方有XX情況"的訊息,這個過程就叫EVENT(含A發訊息以及B接收訊息)。
SCHEDULER 中有兩種觸發EVENT的情況: Scheduler 觸發的Events
Scheduler 中觸發的Events,一般是說當前schduler中job的狀態發生修改,類似job啟動,或者運行結束,或者達到已耗用時間等諸如此類的動作,都能夠拋出一個EVENT,接收到EVENT的applicate就可以根據這些資訊進行適當的處理。
比如說,由於系統太過於繁忙,超出job啟動時間後30分鐘,job仍然沒能順利啟動,那麼這個時候,Scheduler就可以拋出一條EVENT給外部的應用,以便外部應用能夠及時通知DBA,進行處理。 application 觸發的Events
外部的應用也可以觸發Events,並且由Scheduler來接收並處理這一類型的Events。所謂Scheduler處理EVENT就是指Scheduler啟動相應的job來執行相關操作,這類job在建立時專門聲明了event的處理,這樣當接收到EVENT時,這類job就會啟動。
Scheduler 使用Oracle進階隊列來拋出以及銷毀Events。當拋出Schduler觸發的Events時,Scheduler將訊息入隊到預設的event隊列,application則通過檢查該隊列來處理Events。當拋出application觸發的Events時,application將訊息入隊到處理job對應的隊列中。
下面我們也按照這兩個類型來介紹Scheduler中的Events。 4.1 Scheduler拋出的Events
前面說了,Scheduler拋出的Events一般是指job狀態改變時觸發的,那麼是不是說只要job狀態發生了改變,就會觸發Events,其實並非如此,因為預設情況下,job是不觸發Events的。
Scheduler 中的job有一個屬性叫raise_events,專門用來設定job觸發Events的條件,該屬性在CREATE_JOB時不能執行,因此預設情況下該屬性不會賦值,自然也就不會觸發EVENT。要設定raise_events屬性,只能是在job建立完成後,通過SET_ATTRIBUTE過程修改job的raise_events屬性。
例如,修改前面建立的job-,啟用raise_events屬性,執行語句如下:
SQL> BEGIN
2 DBMS_SCHEDULER.SET_ATTRIBUTE('INSERT_TEST_TBL', 'raise_events', DBMS_SCHEDULER.JOB_ALL_EVENTS)
3 END;
4 / PL/SQL procedure successfully completed.
上述樣本中指定的raise_events屬性的屬性值DBMS_SCHEDULER.JOB_ALL_EVENTS,就是拋出Events的觸發條件。
觸發Events的有下列的類型,分別代表不同的操作: job_started :JOB啟動; job_succeeded :JOB成功結束; job_failed :JOB執行失敗; job_broken :JOB被置為BROKEN狀態; job_completed :JOB達到最大運行次數,或者啟動並執行結束日期; job_stopped :JOB被STOP_JOB過程置為停止執行的狀態; job_sch_lim_reached :Job的schedule達到限定值; job_disabled :JOB被置於DISABLE狀態; job_chain_stalled :運行於chain的JOB被置於CHAIN_STALLED狀態; job_all_events :含上述提到的所有類型; job_run_completed :由於Job運行出錯、成功結束或被手動停止。
起用raise_events後,Scheduler就會按照設定的觸發條件,當達到觸發條件時,即會拋出事件資訊到SYS.SCHEDULER$_EVENT_QUEUE隊列。
例如,手動執行一次INSERT_TEST_TBL,看看是否向隊列中記錄資訊,操作如下:
SQL> exec dbms_scheduler.run_job('INSERT_TEST_TBL'); PL/SQL procedure successfully completed.
執行下列指令碼,出隊資料:
SQL> set serveroutput on
SQL> DECLARE
2 l_dequeue_options DBMS_AQ.dequeue_options_t;
3 l_message_properties DBMS_AQ.message_properties_t;
4 l_message_handle RAW(16);
5 l_queue_msg sys.scheduler$_event_info;
6 BEGIN
7 l_dequeue_options.consumer_name := 'TEST';
8
9 DBMS_AQ.dequeue(queue_name => 'SYS.SCHEDULER$_EVENT_QUEUE',
10 dequeue_options => l_dequeue_options,
11 message_properties => l_message_properties,
12 payload => l_queue_msg,
13 msgid => l_message_handle);
14 COMMIT;
15
16 DBMS_OUTPUT.put_line('event_type : ' || l_queue_msg.event_type);
17 DBMS_OUTPUT.put_line('object_owner : ' || l_queue_msg.object_owner);
18 DBMS_OUTPUT.put_line('object_name : ' || l_queue_msg.object_name);
19 DBMS_OUTPUT.put_line('event_timestamp: ' || l_queue_msg.event_timestamp);
20 DBMS_OUTPUT.put_line('error_code : ' || l_queue_msg.error_code);
21 DBMS_OUTPUT.put_line('event_status : ' || l_queue_msg.event_status);
22 DBMS_OUTPUT.put_line('log_id : ' || l_queue_msg.log_id);
23 DBMS_OUTPUT.put_line('run_count : ' || l_queue_msg.run_count);
24 DBMS_OUTPUT.put_line('failure_count : ' || l_queue_msg.failure_count);
25 DBMS_OUTPUT.put_line('retry_count : ' || l_queue_msg.retry_count);
26 END;
27 /
event_type : JOB_STARTED
object_owner : TEST
object_name : INSERT_TEST_TBL
event_timestamp: 25-AUG-09 12.49.29.558758 PM +08:00
error_code : 0
event_status : 1
log_id :
run_count : 1
failure_count : 0
retry_count : 0 PL/SQL procedure successfully completed.
從返回的資訊可以看到,event的類型為JOB_STARTED,表示JOB啟動。實際上job:INSERT_TEST_TBL執行一次至少會向隊列中插入兩條event資訊,一條為JOB_STARTED,一條則為JOB_SUCCEEDED(也可能是JOB_FAILED),這裡不詳細示範,感興趣的朋友不妨自行測試。 提示:SYS.SCHEDULER$_EVENT_QUEUE隊列基於SYS.SCHEDULER$_EVENT_QTAB隊列表,因此查詢SYS.SCHEDULER$_EVENT_QTAB也可以擷取上述的資訊。
SYS.SCHEDULER$_EVENT_QUEUE 是一個固定隊列,實際應用的過程中,DBA應該根據實際情況,將該表存取權限授予相關使用者,以便順利出隊該隊列中的events資訊。
另外,友情提醒,預設情況下Scheduler僅保留最近24小時的Events資訊,如果希望修改該設定的話,可以通過SET_SCHEDULER_ATTRIBUTE過程,修改scheduler的event_expiry_time屬性,該項屬性的屬性值以秒為單位。 4.2 Application拋出的Events
首先要說明,這裡所說的Application是個代詞,即可以表示ORACLE資料庫之外的應用程式,也可以是ORACLE資料庫中的PROCEDURE等對象,總之你就將其理解成使用者自己建立的對象就好了。
Scheduler 能夠拋出Events讓外部應用處理,外部的應用也可以拋出Events讓Scheduler啟動job處理,不過並不是任何job都能夠對外部應用拋出的Events做出響應,必須在建立jobs時明確指定響應的事件。那麼如何指定呢。依靠下列兩個附加的參數: queue_spec :指定外部應用拋出的events訊息入隊的隊列名; event_condition :指定觸發job啟動的條件,這一參數的參數值在設定時應當基於事件訊息的自身屬性,因為事件訊息在入隊時,訊息的屬性都是由application定義的,因此在設定觸發條件時,也應該根據這些屬性值就行設定。
下面,我們就示範建立一個由event觸發啟動的job,在此之前,首先需要進行一些準備工具,比如建立隊列,由於隊列需要基於一個隊列表,因此在建立隊列之前,首先要建立一個隊列表,考慮到隊列表需要依賴一個物件類型,因此在建立隊列表之前,先得建立一個type.......複雜,具體的操作步驟如下,客官可要看仔細了:
SQL> create or replace type jss_type 1 as object
2 (
3 event_type VARCHAR2( 1 0),
4 object_owner VARCHAR2( 30 ),
5 object_name VARCHAR2( 3 0)
6 );
7 /
Type created.
SQL> begin
2 dbms_aqadm.create_queue_table(
3 queue_table => 'my_queue_tbl1',
4 queue_payload_type => 'JSS_TYPE1',
5 multiple_consumers => true);
6 end;
7 /
PL/SQL procedure successfully completed.
SQL> begin
2 dbms_aqadm.create_queue(
3 queue_name => 'event_t1',
4 queue_table => 'my_queue_tbl1');
5 end;
6 / PL/SQL procedure successfully completed.
OK, 準備工作完成,下面就來建立一個event觸發啟動的job,建立指令碼如下:
SQL> BEGIN
2 DBMS_SCHEDULER.CREATE_JOB (
3 job_name => 'EVENT_JOB_T1',
4 job_type => 'STORED_PROCEDURE',
5 job_action => 'P_INSERTINTOTEST',
6 event_condition => 'tab.user_data.event_type = ''OP_INSERT''',
7 queue_spec => 'EVENT_T1',
8 enabled => TRUE);
9 END;
10 / PL/SQL procedure successfully completed.
上述指令碼僅做示範,因此建立的job仍然執行P_INSERTINTOTEST過程。
三思並不準備再編寫一套外部的應用來觸發,這裡僅為了示範application觸發job啟動的樣本,因此三思決定通過pl/sql直接向event_t1隊列中添加訊息的方式,觸發job的啟動,具體操作如下。
首先要執行DBMS_AQADM.START_QUEUE過程,將event_t1置於允許入隊和出隊狀態(預設情況下建立的隊列是不允許出隊和入隊操作的),指令碼如下:
SQL> exec dbms_aqadm.start_queue(queue_name => 'event_t1',enqueue => true,dequeue => true); PL/SQL procedure successfully completed.
執行入隊操作:
SQL> declare
2 v_Message jss_type1;
3 v_EnqueueOptions dbms_aq.enqueue_options_t;
4 v_MessageProperties dbms_aq.message_properties_t;
5 v_msg_handle raw(16);
6 begin
7 v_message := jss_type1('OP_ SELECT ', user, 'tmpObj');
8
9 dbms_aq.enqueue(queue_name => 'event_t1',
10 enqueue_options => v_enqueueOptions,
11 message_properties => v_messageproperties,
12 payload => v_message,
13 msgid => v_msg_handle);
14 commit;
15
16 end;
17 / PL/SQL procedure successfully completed.
查詢隊列表中的資料: