[PLSQL]DBMS_PIPE at a glance

扯在前面

之前提到过Advanced Queue可以用来多个session之间的信息传递,AQ本质上还是依赖于queue table。DBMS_PIPE也可以用来做inter-session的信息传递,但是比较AQ来说,个人感觉还是相当轻量级的。DBMS_PIPE顾名思义,提供像pipe的功能,跟AQ比较起来,数据都是存在内存里面的,因此持久性不强,另外也不提供callback方式来读取信息。

通过DBMS_PIPE发送接收信息的大概流程如下:信息发送方首先在自己当前session的UGA中打包(pack)要传递的信息,完了之后,通过“指定”的pipe将数据send到SGA中,这样其他session就可以通过相同的pipe连接到SGA中的区域将数据“打包”地取回到自己session的UGA中,然后解包(unpack)读取其中的数据信息。

DBMS_PIPE相关接口

DBMS_PIPE主要提供如下一些程序接口,

- CREATE_PIPE function (necessary for private pipes)

- REMOVE_PIPE function

- PACK_MESSAGE procedure   (builds message in local buffer)

- UNPACK_MESSAGE procedure ( accesses next item in buffer)

- SEND_MESSAGE function  (sends message on named pipe: this implicitly creates a public pipe if the named pipe does not exist)

- UNIQUE_SESSION_NAME function  (returns unique session name)

- PURGE procedure (purges contents of named pipe)

有一点要特别注意的是, pipe根据访问权限可以分为public pipeprivate pipe, 根据创建方式又可以分为explicit pipeimplicit pipe.

- implicit pipe (通过SEND_MESSAGE创建)都是 public pipe. 如果要创建private pipe,必须通过PACK_PIPE.create_pipe来创建。

- implicit pipe 当pipe空的时候会自行消失

- explicit pipe 必须通过pack_pipe.remove_pipe来删除

- public pipe 可以被所有对dbms_pipe拥有execute权限的user访问

- private pipe 只能够被该pipe创建者的session来访问或者是拥有sysdba权限的用户。

DBMS_PIPE in Action

首先看看如何显示地创建public/private pipe, 可以通过视图v$db_pipes 来查看创建的pipe,注意到public pipe的ownerid 的空的。

必须通过dbms_pipe.remove_pipe来删除显示创建的pipe.


1 declare
2 l_result integer;
3 begin
4 l_result := dbms_pipe.create_pipe(pipename=> 'explicit_public_pipe', private=> false);
5 l_result := dbms_pipe.create_pipe(pipename=> 'explicit_private_pipe');
6* end;
7 /

PL
/SQL procedure successfully completed.

SQL
> col name for a30
SQL
> select * from v$db_pipes;

OWNERID NAME TYPE PIPE_SIZE
---------- ------------------------------ ------- ----------
292 EXPLICIT_PRIVATE_PIPE PRIVATE 1430
EXPLICIT_PUBLIC_PIPE
PUBLIC 1429

SQL
> declare
2 l_result integer;
3 begin
4 l_result := dbms_pipe.remove_pipe(pipename=>'explicit_public_pipe');
5 l_result := dbms_pipe.remove_pipe(pipename=>'explicit_private_pipe');
6 end;
7 /

PL
/SQL procedure successfully completed.

SQL
> select * from v$db_pipes;

no rows selected

SQL
>

接下来看一个package, 里面包含了send/receive message的调用接口。因为pipe中的message是通过dbms_pipe.pack_message来一点一点打包的,而接收方必须按照相同的方式来一点一点unpack这个message,如果只是提供send接口,那么接收方很难知道怎么去读取pipe中的信息,很容易出错。因此同时提供send/receive的实现是一个很好的idea.

SQL> create or replace package message_api as
2 procedure send(vv_number in number,
3 vv_text in varchar2,
4 vv_date in date default sysdate);
5 procedure receive;
6 end message_api;
7 /

Package created.
1 create or replace package body message_api as
2 procedure send(vv_number in number,
3 vv_text in varchar2,
4 vv_date in date default sysdate)
5 as
6 v_status number;
7 begin
8 dbms_pipe.pack_message(vv_number);
9 dbms_pipe.pack_message(vv_text);
10 dbms_pipe.pack_message(vv_date);
11 v_status := dbms_pipe.send_message('message_pipe');
12 if v_status != 0 then
13 raise_application_error(-20001, 'message_pipe error');
14 end if;
15 end;
16 procedure receive as
17 v_result integer;
18 v_number number;
19 v_text varchar2(32767);
20 v_date date;
21 begin
22 v_result := dbms_pipe.receive_message
23 ( pipename => 'message_pipe',
24 timeout => dbms_pipe.maxwait);
25 if v_result = 0 then
26 dbms_pipe.unpack_message(v_number);
27 dbms_pipe.unpack_message(v_text);
28 dbms_pipe.unpack_message(v_date);
29 dbms_output.put_line('v_number: ' || v_number);
30 dbms_output.put_line('v_text: ' || v_text);
31 dbms_output.put_line('v_date: ' || v_date);
32 else
33 raise_application_error(-20002, 'message_api.receive was unsuccessful');
34 end if;
35 end receive;
36* end message_api;
SQL
> /

Package body created.

接下来进行简单的测试一下....

在session 1中调用send接口,往pipe里面send一条message,

SQL> begin
2 message_api.send(vv_number => 11111, vv_text=> 'Test', vv_date=> sysdate);
3 end;
4 /

PL
/SQL procedure successfully completed.

在session 2中调用receive接口来接收message,

SQL> exec message_api.receive;
v_number:
11111
v_text: Test
v_date:
06-NOV-10

PL
/SQL procedure successfully completed.

两个session哪个先启动无所谓,不过要注意的是receive的时候如果发现pipe里面没有message会进行等待 (session is hanging)

另外如果send了多条message,同样也要通过多次receive来获取全部的message。

说明

本文的例子来源 http://www.oracle-base.com/articles/misc/dbms_pipe.php




--------------------------------------
Regards,
FangwenYu
原文地址:https://www.cnblogs.com/fangwenyu/p/1870665.html