这是我使用 Oracle 高级队列的第一步...
Szenario:我有一个正在运行的应用程序,其中有很多很多独立的进程向中央控制器报告以处理后续步骤。简化的进程是通过 cron 或刚刚完成的进程的回调启动的。回调来自远程主机通过 http -> php -> DB,基本上是远程主机上进程完成后的一个 http 调用。
完整的控制器逻辑是用 pl/sql 编写的,并考虑了单例概念,因此只有一个进程应该同时执行控制器逻辑。事实上,在 99% 的调用中,这不是必需的,但这不是我目前可以改变的事情(也不是一般的架构)。
为了确保这一点,实际上存在一个错误的互斥锁实现,伪代码
$mutex = false;
while( not $mutex )
{
$mutex = getMutex();
if( $mutex )
executeController();
else
sleep(5);
}
其中互斥锁是一个字段表,其值为 0 (=> "free") 或 1 ( => "busy" )
这种“美丽”构造的结果是充满“嘿!没有互斥锁!等待......”的日志文件。而且等待的进程越多,它们等待的时间越长,无法控制下一个是谁。有时负载变得如此沉重以至于apache首先分叉并最终死掉......
解决方案
所以我的第一个“操作”是用 Oracle Advanced Queuing 替换互斥锁,控制器作为单一消费者。好处:apache层内不再“忙着等待”,严格先到先得。
(因为所有 DB-Action 都发生在同一个 oracle-schema 中,这可以通过标准对象、pl/sql-methods 来实现。但是,如果有 dbms-packages,为什么要重新发明轮子呢?)
据我所知,在这种情况下使用监听功能(轮询排队的项目)比注册功能(在消息到达时安排动作)要好得多。
基本上一切正常,我设法:
- 创建消息类型
- 创建队列表
- 创建队列
- 开始排队
- 将 USER 添加为订阅者
- 创建入队过程
- 创建处理和出队的过程
- 创建一个用于侦听队列并在消息到达时调用“process & dequeue”函数的过程。
当然,监听器应该 24/7 都处于活动状态,所以我没有指定“等待”时间。一般来说,根据一天中的时间,他至少每隔几分钟就会有“要做的事情”,更有可能每隔几秒钟,有时甚至更多。
现在这是我的问题(如果它确实是一个问题),我只是根据我目前找到的示例编写它:
CREATE OR REPLACE PROCEDURE demo_aq_listener IS
qlist dbms_aq.aq$_agent_list_t;
agent_w_msg sys.aq$_agent;
BEGIN
qlist(0) := sys.aq$_agent(USER, 'demo_aq_queue', NULL);
LOOP
dbms_aq.listen(agent_list => qlist, agent => agent_w_msg);
DEMO_AQ_DEQUEUE();--process & dequeue
END LOOP;
END;
/
调用该过程基本上符合我的预期:它保持“启动”并处理排队的消息。
但这是做到这一点的方法吗?如果没有排队的消息,它会做什么?dbms_aq.listen -routine中的“睡眠”或“尽可能快地循环”,这样我就实现了另一种“忙于等待”的方式?可能有一个超时(可能在 oss 级别或其他地方)我只是没有达到?
这是队列定义等的完整代码:demo_dbms_aq_with_listener.sql
更新
通过进一步的测试,我才意识到,我似乎比我希望的更缺乏理解:(
在“执行级别”根本不使用侦听器,只是循环出队函数具有相同的效果:它等待第一条/下一条消息
CREATE OR REPLACE PROCEDURE demo_aq_listener IS
BEGIN
LOOP
DEMO_AQ_DEQUEUE();
END LOOP;
END;
/
至少这更容易测试,只调用
BEGIN
DEMO_AQ_DEQUEUE();
END;
/
也只是等待第一条消息。这让我完全困惑我是否需要听众,如果我正在做的事情确实有任何意义:(
结论
我根本不需要监听器,因为我有一个消费者可以以同样的方式处理所有消息。
但是关键/核心问题保持不变:是否可以将 DBMS_AQ.DEQUEUE 保持在“可能处于活动状态”的循环中,知道它会在很短的时间间隔内整天收到消息?
(你会在上面链接的 sql 文件中找到 DEMO_AQ_DEQUEUE())