用一个简单的例子,来看一下Listen/Notify如何使用。假设我们有两个应用A和B,部署在不同的机器上:A机器处理前端用户请求,同时需要将一些可以异步执行的任务,分配给后台服务器B。B接收到任务并处理完成后反馈给A结果。

利用上面的步骤,A和B两个机器通过PG完成了通信。在上面的过程中,需要注意的是:

  1. A需要使用事务commit操作来触发消息发送;
  2. 消息是异步发送到B的,即无论B的状态如何,消息都会先到达PG的消息队列(每个PG实例只有一个唯一的存放所有消息的队列);B要查看消息,如果使用的是psql客户端,则需要先发送带有事务操作的命令(如begin、commit或rollback)给PG;
  3. 在C代码里面,你可以使用如下的调用来获取所有已到达的消息,如果没有消息到达,则进入睡眠状态。

消息队列里面存放了所有进程的所有通知消息,而状态数组存放了所有执行了Listen命令、准备接收异步消息的进程的状态信息。状态数组中含有每个进程已经读取到的消息在队列里面的位置指针。如果有了新消息,进程就从此指针往后取,直到读取全部消息。

执行Notify命令时,Async_Notify函数负责把通知放入pendingNotifies链表。在事务Commit操作前后,执行下面的逻辑:

  1. 调用PreCommit_Notify函数,将pendingNotifies链表中的消息,放入全局消息队列;
  2. 利用调用链ProcessCompletedNotifies->SignalBackends->SendProcSignal->kill,向其他所有状态数组中的进程,发出通知信号。

另一方面,每个进程在接收到信号后,利用函数HandleNotifyInterrupt处理信号。如果当前进程处于事务中,则不立即处理消息,等到事务提交完毕,调用读取下一个用户命令时,利用ProcessIncomingNotify处理消息;否则,立即调用ProcessIncomingNotify处理消息。最终调用NotifyMyFrontEnd发送消息到客户端:

Listen/Notify是一个轻量级的应用间通信机制,有了它,具有访问数据库能力的应用可以轻易的利用PG实现互操作。当然,由于消息队列是存放在内存里面的,在发生实例宕机等问题时,消息将丢失,对可靠性要求高的应用,需要自己进行消息持久化(如利用PG存储消息,进行持久化)。