简介
在我的前一篇文章 在 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 | 没有访问该管道的权限
您可能想查找下面的文章: |