簡介
在我的前一篇文章 在 DB2 通用數據庫中實現會話間警告 中,您看到了如何使用兩個數據庫會話之間的警告(alert)來實現一條通信通道。本文為數據庫會話間的通信提供了另一種實現,這種實現使用消息管道。警告和管道兩者都可以在異步和同步兩種模式之一中發揮作用。兩者的不同之處是,管道可以將單個消息的分組(packet)從一個會話傳輸到另一個會話,而使用警告是不可能做到這一點的。警告多用於根據另一個會話中的事件采取某種行動。通過使用管道,您可以將由不同數據類型組成的消息分組實實在在地從一個會話傳輸到另一個會話。這種實現類似於 Oracle DBMS_PIPE 包,但還是有些變化。如果您要將應用程序從 Oracle 移植到 DB2 Universal Database,那麼這種實現非常有用。
這種管道包(pipe package)的實現包含:
3 個 catalog 表
2 個用戶定義函數
28 個 SQL 存儲過程
工作原理
這裡應該有兩個參與的會話。其中一個會話(假設為 A)准備好消息分組並將其發送到數據庫編目(catalog)。另一個會話(假設為 B)讀取由 A 發送的消息分組,然後關閉管道。
會話 A 做以下事情:
創建一個命名管道,可以使用大小限制,也可以不使用大小限制,管道的類型可以是 public 或 private 。
將消息打包到一個命名的管道或臨時的管道中,這裡要求管道是打開的,並且存在於此會話中。 如果命名的管道不存在,那麼它就創建一個臨時的 public 類型的大小不限的管道,並打包消息。如果該管道的類型為 private ,那麼此管道中的所有消息都要加密。
如果消息分組由多條消息組成,則繼續打包。
發送該消息分組。如果管道是一個臨時管道,那麼為此臨時管道提供一個名字。
會話 B 做以下事情:
接收命名的管道並打開它。
檢查消息的數據類型。檢查和讀取消息的順序是 FIFO (First In First Out,先進先出)。在檢查了數據類型之後,會話 B 將相同的信息通知給調用應用程序。
之後調用者應用程序將消息解包並存儲在一個適當的變量(數據類型非常重要)中。如果管道的類型為 private ,那麼先要對消息解密,然後再讀取它。如果在管道中再沒有其他的消息,則關閉管道。否則,繼續重復執行步驟 2 和步驟 3。
除了上述功能行為之外,還有另外三個可用於管道管理的功能:
創建命名管道的用戶可以刪除處於關閉狀態的這個管道。
不管命名管道是由哪個用戶創建的,數據庫管理員都可以刪除處於關閉狀態這個管道。
創建命名管道的用戶可以清除和重新使用處於關閉狀態的這個管道。
圖 1. 創建、打包和發送一個管道
查看原圖(大圖)
圖 2. 接收、檢查一個管道並對這個管道解包
查看原圖(大圖)
設計視圖
在 DB2 Universal Database 中實現了三種類型的對象,用以支持管道功能。本文的下載文檔中提供了所有這些對象。
catalog 表
DBMS_PIPE.PIPES——存儲管道的定義
DBMS_PIPE.PIPEMEMBERS——以純字符串格式存儲消息
DBMS_PIPE.PIPEMEMBERS_ENCRYPT——以加密字符串格式存儲消息
用戶定義的函數
DBMS_PIPE.DELAY——提供按秒計的延時
DBMS_PIPE.SESSION_ID——從數據庫管理器中捕捉會話 ID
注意: 在 DB2 通用數據庫中實現會話間警告 一文中提供了這兩個函數的源代碼。這裡同樣使用了這兩個函數,但這些函數已經是經過編譯的。
SQL 存儲過程
DBMS_PIPE.CREATE_PIPE——創建一個命名管道,3 Nos,被重載
DBMS_PIPE.PACK_MESSAGES_xxxx——將消息打包到管道中,9 Nos,用於不同數據類型
DBMS_PIPE.SEND_MESSAGE——發送管道以用於處理,2 Nos,被重載
DBMS_PIPE.RECEIVE_MESSAGE——檢索管道以用於讀取
DBMS_PIPE.NEXT_ITEM_TYPE——檢查第一個打包的而未檢查過的消息數據類型
DBMS_PIPE.UNPACK_MESSAGES_x——讀取消息並關閉管道,9 Nos,用於不同數據類型
DBMS_PIPE.PURGE——清除管道中的消息並將管道狀態重置為 PACKING
DBMS_PIPE.REMOVE_PIPE——由創建者從系統中刪除一個管道
DBMS_PIPE.DELETE_PIPE——由管理員刪除一個管道
DBMS_PIPE.PIPES catalog 表有 8 個字段:
PID——管道 ID
PNAME——管道名
PTYPE——管道類型(Y - Private,N - Public)
POWNER——管道創建者的名字
PSTATUS——管道的狀態(P - Packing, S - Sent, O - Open, R - Reading, C - Closed)
PNDATA——管道中消息的編號
PSIZE——管道的大小限制(按字節計)
PTEMPNAME——管道的臨時名字(設置為會話 ID)
DBMS_PIPE.PIPEMEMBERS catalog 表有 6 個字段:
SLNO——一個序列號
PID——管道 ID(使用外鍵鏈接到 PIPES.PID)
MID——管道中的成員/消息 ID
MTID——成員/消息數據類型 ID(下面將提到)
NDIF——成員/消息的狀態(N:未檢查,C:已檢查,R:讀取)
DATA——成員/消息數據(純字符串格式)
DBMS_PIPE.PIPEMEMBERS_ENCRYPT catalog 表也具有相同的字段,但是它以加密字符串的格式將數據存儲在 DATA 字段中。
在下載中提供了四個實用程序。以 SYSADM 身份從 COMMAND WINDOW 執行這些程序,不要從 MS-DOS 窗口執行:
pipesetup.bat——編譯所有的存儲過程並注冊前面提到的所有對象。
getroutines.bat——將所有 SQL 存儲過程以二進制格式導出到 sar 文件中。
putroutines.bat——從 sar 文件導入所有 SQL 存儲過程,但是不重新編譯這些存儲過程。
cleanup.bat——從數據庫系統清除整個管道系統實現。
注意:為了編譯 SQL 存儲過程,需在服務器上提供 VC++ v5.0/v6.0 或 CYGWIN (Windows 下模擬的 Linux) GCC 以及 Java 編譯器。如果沒有 VC++ 編譯器,那麼可以使用 CYGWIN 的 GCC 編譯器來編譯同樣的存儲過程。
不同點、優點和局限性
管道功能是通過使用三個 catalog 表來實現的,這裡並沒有用到任何共享內存。因此,這些管道更加穩定,不受可用內存大小的影響。而且這種實現也更可靠,因為這裡不存在對共享內存的直接訪問途徑,所有的數據操作都是由數據庫服務器完成的。
對於消息的大小有一定的限制。每條消息最大可以達到 32631 字節。但是消息分組的大小(即管道大小)不存在任何大小限制,而在 Oracle 中,管道最大只能為 4 KB。
在對消息打包和解包時,需要不同的存儲過程,因為存儲過程是不能重載的。
數據類型為 LONG 或 LOB 的消息不能打包到管道中。不過,有一個方法可以做這件事。因此您可以同樣實現它。
這個功能還可以在一個分為多個分區的環境中實現。在所有表中都應選擇 PID 作為分區鍵(partitioning key),以使針對一個管道的所有信息都存儲在相同的數據庫分區中。
錯誤代碼和返回代碼
在出現錯誤時,只要在 DB2 Universal Database 文檔中定義了錯誤代碼,作為管道實現包的一部分而提供的例程(用戶定義的函數和存儲過程)都可以返回該錯誤代碼。此外,以下是由例程返回的代碼。
如果所有事項都是成功的,則返回0,但 NEXT_ITEM_TYPE 存儲過程除外,該過程在成功時返回一個正的 5 位數值。每個數值代表一種數據類型:
數值 數據類型 99388 time 99384 date 99392 timestamp 99452 char (最多 254 個字符) 99448 varchar (最多 32631 字符) 99500 smallint 99496 int 99492 bigint 99484 double
無論出現何種錯誤,用戶定義函數都將返回 -1。
上述存儲過程除了返回以下 DB2 Universal Database 服務器錯誤代碼外,還返回一些用戶定義的錯誤代碼。
錯誤代碼 解釋 -9801 被調存儲過程的第一個參數是非法的 -9802 被調存儲過程的第二個參數是非法的 -9803 被調存儲過程的第三個參數是非法的 -9989 管道正在使用中,不能清除消息,也不能刪除管道 -9990 管道中沒有消息 -9991 沒有“打開”的管道可供讀取消息 -9992 沒有接收消息的管道,或者管道不處於 SENT 狀態 -9993 沒有訪問該管道的權限 -9994 管道名不匹配 -9995 當前會話中沒有可以發送的打開的管道 -9996 該管道名已經在使用 -9997 當前管道沒有足夠的空間 -9998 當前管道不處於 PACKING 狀態 -9999 當前會話中至少有一個打開的管道
安裝步驟
在 DB2 Universal Database 中可以用兩種方法來實現這個‘pipe’功能:
使用實用程序 ....\\pipe\\sourceform\\pipesetup.bat 。
像下面這樣執行該實用程序: Pipesetup.bat <database-name> <SYSADM userid> <SYSADM passWord>
例子:
pipesetup.bat sample TEST TEST01
使用實用程序 ....\\pipe\\compiledform\\putroutines.bat .
像下面這樣執行該實用程序: Putroutines.bat <database-name> <SYSADM userid> <SYSADM passWord>
例子:
putroutines.bat sample TEST TEST01
在執行上述任何一個實用程序前,都要做以下事情:
在文本編輯器(例如記事本)中打開實用程序。
更改這一行:
set DB2HOMEPATH=C:\\SQLLIB
,使其指向系統中正確的 SQLLIB 路徑。
更改這一行:
DB2 -o- CALL SQLJ.INSTALL_JAR('file:C:\\Documents and Settings\\sudipta\\My Documents\\db2\\KnowledgeBase\\article5_inprogress\\pipe\\sourceform\\delayUDF.jar','delayUDFjar');
,使其指向 delayUDF.jar 文件所在的正確位置。
測試步驟
為了測試該管道系統:
打開兩個 DB2 COMMAND WINDOW 會話(啟用了 DB2 環境之後的命令窗口)。我們將其中一個會話命名為 Session A,另一個會話命名為 Session B。使用 SYSADM userid 連接到樣本數據庫(如果沒有樣本數據庫,那麼使用 db2sampl 命令創建一個)。如果您還沒有創建‘pipe’系統,那麼使用上述信息創建一個這樣的系統。如果您將要多次運行該系統,那麼不需要一次又一次地編譯存儲過程。在再次運行測試之前,應通過執行以下命令刪除管道詳細信息: db2 delete from dbms_pipe.pipes where pname='ABC'
.
在 session A 中:
定位到 .....\\pipe\\testscripts 目錄。
執行命令: db2 -td@ -f send_packet.sql
該命令編譯一個存儲過程,這個存儲過程負責創建一個管道,打包兩條消息,並發送消息。
執行命令: db2 call dbms_pipe.send_packet()
您應該可以看到一個 0 (零)返回狀態。
選擇表 dbms_pipe.pipes和 dbms_pipe.pipemembers以查看詳細信息。
在 session B 中:
定位到 .....\\pipe\\testscripts 目錄。
執行命令 db2 -td@ -f read_packet.sql 。該命令編譯一個存儲過程,這個存儲過程負責接收一個管道,檢查數據類型,解包兩條消息,最後關閉管道。
執行命令 db2 call dbms_pipe.read_packet(?, ?) 。您應該可以看到一個 -999010 返回狀態(因為管道在檢索最後一條消息,即 DATA2 之後被關閉),同時返回的還有 DATA1(time) 和 DATA2(date) 的數據。
選擇表 dbms_pipe.pipes和 dbms_pipe.pipemembers以查看詳細信息。
重要提示:
整個實現將使用 DB2 UDB 錯誤消息發送方法/代碼來處理由於 SQL 錯誤或 DB2 UDB 系統中意外問題而引起的錯誤行為。除此之外,該實現還提供了作為管道功能行為一部分的定制錯誤代碼和返回代碼。
任何錯誤(邏輯功能錯誤、SQL 錯誤或 DB2 UDB 系統錯誤)都是以存儲過程或函數的返回代碼(數值)的形式被捕捉到。管道的邏輯錯誤是使用 -98XX 和 -99XX 錯誤代碼系列捕捉的,對此 DB2 UDB 沒有提供任何實現。只有存儲過程 dbms_pipe.next_item_type 返回一個 5 位的數值代碼,用以檢查數據類型,並使用 dbms_pipe.unpack_message_x 存儲過程采取行動。在本例中,DB2 UDB 沒有提供任何使用 99XXX 系列的錯誤消息實現,因此在錯誤消息發送時不存在沖突問題。
錯誤代碼的定義嵌入在每個源代碼文件中。您可以在實現時靈活地選擇自己的錯誤代碼。
本文示例源代碼或素材下載