-2

我对 Postgre 触发器有问题或误解 -> 执行通知 -> 捕获到 PHP 流中。

我的平台是带有 Postgres 的 centos 中的 PHP(5.6)。

我必须添加带有通知表的触发器,并且每当向该通知中添加 新通知时, SMS 都必须发送给该用户。

所以这里添加了这样的触发器

CREATE FUNCTION xxx_sms_trigger() RETURNS trigger
    LANGUAGE plpgsql
    AS $$
DECLARE
BEGIN
  PERFORM pg_notify('sms', NEW.id||'' );
  RETURN new;
END;

在 php 中插入新通知工作正常。

现在我有一个单独的文件,其中添加了由 "pg_get_notify" 触发的捕获 pg_notify,在这里我无法完全像 Postgres 如何触发一些未知的 php 脚本而不将其作为服务运行或者我如何使它工作一样?

4

5 回答 5

3

您确实需要一个作为服务运行的 php 脚本。如果那将是接收您提供的通知的语言。正如@FelipeRosa 所说,该脚本需要连接到数据库,然后发出至少一个命令:

listen sms;

主站点上有一个很好的监听示例(http://www.php.net/manual/en/function.pg-get-notify.php

我已经有几年没有用 php 编码了。最近我在python中实现了这个逻辑,但是应该差不多。我做了一点研究,在php中可以找到select(),但是好像php中没有postgres socket描述符,所以不能在php中使用select(),除非能找到postgres socket描述符.

无论如何,该线程在这里(http://postgresql.1045698.n5.nabble.com/Is-there-any-way-to-listen-to-NOTIFY-in-php-without-polling-td5749888.html)。那里有一个轮询示例,您的 php 脚本朝下靠近底部。您可以按照先前选择的方式进行监听(一次),然后将您的 pg_get_notify() 放入一个循环中,并在其中休眠一段时间,直到您愿意排队通知。

只是fwiw,在python中我不轮询,我select.select(pg_conn,...),当数据到达postgres连接时,我检查它是否有通知,所以没有“轮询”。如果您能找到一种在 php 中使用 select() 而不是循环的方法,那就太好了。

-G

于 2014-05-09T20:09:58.740 回答
3

这是一个连贯的示例,它注册对表插入的兴趣,等待通知(或超时)并响应调用者。我们使用前面带有字母“C”的时间戳来标识通知通道,因为 Postgres 要求通道名称是正确的标识符。

Postgres SQL

/*  We want to know when items of interest get added to this table.   
    Asynchronous insertions possible from different process or server  */
DROP TABLE IF EXISTS History;
CREATE TABLE History (
   HistoryId INT PRIMARY KEY,
   MYKEY CHAR(17),
   Description TEXT,
   TimeStamp BIGINT
);

/*  Table of registered interest in a notification  */
DROP TABLE IF EXISTS Notifications;
CREATE TABLE Notifications (
   NotificationId INT PRIMARY KEY,
   Channel VARCHAR(20),
   MYKEY CHAR(17)
);

/*  Function to process a single insertion to History table  */
CREATE OR REPLACE FUNCTION notify_me()
  RETURNS trigger AS
$BODY$
  DECLARE ch varchar(20);
  BEGIN
     FOR ch IN
     SELECT DISTINCT Channel FROM Notifications 
        WHERE MYKEY=NEW.MYKEY
     LOOP
        /* NOTIFY ch, 'from notify_me trigger'; */
        EXECUTE 'NOTIFY C' || ch || ', ' || quote_literal('from notify_me') || ';';
        DELETE FROM Notifications WHERE Channel=ch;
     END LOOP;
     RETURN NULL;
  END;
$BODY$
LANGUAGE 'plpgsql';

/*  Trigger to process all insertions to History table  */
DROP TRIGGER IF EXISTS HistNotify ON History CASCADE;
CREATE TRIGGER HistNotify AFTER INSERT ON History
    FOR EACH ROW EXECUTE PROCEDURE notify_me();

PHP 代码

// $conn is a PDO connection handle to the Postgres DB
// $MYKEY is a key field of interest
$TimeStamp = time();  //  UNIX time (seconds since 1970) of the request
$timeout = 120;  //  Maximum seconds before responding

//  Register our interest in new history log activity
$rg = $conn->prepare("INSERT INTO Notifications (MYKEY, Channel)  VALUES (?,?)");
$rg->execute(array($MYKEY, $TimeStamp));

//  Wait until something to report
$conn->exec('LISTEN C'.$TimeStamp.';');  //  Prepend ‘C’ to get notification channel
$conn->exec('COMMIT;');  //  Postgres may need this to start listening
$conn->pgsqlGetNotify (PDO::FETCH_ASSOC, $timeout*1000);  //  Convert  from sec to ms

//  Unregister our interest
$st = $conn->prepare("DELETE FROM Notifications WHERE Channel=?");  
$st->execute(array($TimeStamp));
于 2015-11-19T22:55:39.363 回答
2

这是一个如何将@Greg 提到的“Python 方式”迁移到 PHP 的示例。启动下面的脚本后 - 打开到 postgres db 的新连接并查询NOTIFY "test", 'I am the payload'

资料来源:


<?php

$dsn = 'user=postgres dbname=postgres password=postgres port=5432 host=localhost';

$connection = \pg_connect($dsn);

if (\pg_connection_status($connection) === \PGSQL_CONNECTION_BAD) {
    throw new \Exception(
        sprintf('The database connect failed: %s', \pg_last_error($connection))
    );
}

\pg_query('LISTEN "test"');

while (true) {
    $read = [\pg_socket($connection)];
    $write = null;
    $except = null;
    $num = \stream_select(
        $read,
        $write,
        $except,
        60
    );
    if ($num === false) {
        throw new \Exception('Error in optaining the stream resource');
    }
    if (\pg_connection_status($connection) !== \PGSQL_CONNECTION_OK) {
        throw new \Exception('pg_connection_status() is not PGSQL_CONNECTION_OK');
    } elseif ($num) {
        $notify = \pg_get_notify($connection);
        if ($notify !== false) {
            var_dump($notify);
        }
    }
}
于 2017-12-17T14:21:47.117 回答
0

根据这一点,您应该首先让应用程序侦听发出命令“LISTEN”的所需通道,例如通过 pg_query,然后才能将消息通知给应用程序。

于 2014-05-09T19:06:11.480 回答
0

这是一个小例子:

PHP 脚本(我将其命名为 teste.php - 它与http://php.net/manual/pt_BR/function.pg-get-notify.php相同):

$conn = pg_pconnect("dbname=mydb");
if (!$conn) {
  echo "An error occurred.\n";
  exit;
}

while(true){
   pg_query($conn, 'LISTEN SMS;');
   $notify = pg_get_notify($conn);
     if (!$notify) {
       echo "No messages\n";
       // change it as u want
     } else {
       print_r($notify);
       //your code here
     }
     sleep(2);
}

保持脚本运行(我假设你使用的是 linux):

php teste.php > log.txt 2>&1 &

注意:

2>&1将标准输出和标准错误都重定向到 log.txt 文件中。

&在后台运行整个事情

您可以使用以下命令跟踪 log.txt:

tail -f log.txt
于 2017-04-05T13:40:32.273 回答