等到新的 Sqlite 事件发生

问题描述

我希望 PHPsqlite 数据库中插入新行时等待并执行操作。

当然,我们可以这样做:

while (true) {
    usleep(200*1000);  // wait 200 ms
    $db->query('SELECT id ....');  // check if a new row (with a new id) has been inserted
}

但我想避免每 200 毫秒无故查询一次数据库,因为这会浪费资源。

上下文:我想在长轮询方案中执行此操作,并避免让 PHP 工作程序/守护程序不断运行:客户端与服务器建立连接,服务器不会立即响应并等待新的事情发生数据库

注意:之前我已经使用过WebSocket,但是对于这个特定的项目,我不能使用它。

解决方法

您说过所有可能更改您的数据库的 PHP 脚本都可以启动一个进程来发出更改信号。这意味着您不必经常轮询数据库,您可以轮询其他一些简单的东西。

假设每次对数据库进行更改时,都会更新一个包含时间信号的文件。一个可以做到这一点的 PHP 函数是:

function signalDatabaseChange()
{
    $success = FALSE;
    if ($file = fopen('signal.json','w')) 
    {
        $json = json_encode(["timeSignal" => hrtime(TRUE)]);
        if (flock($file,LOCK_EX)) {
            $success = fwrite($file,$json) > 0;
            flock($file,LOCK_UN); 
        }
        fclose($file);
    }
    return $success;
} 

它将写入一个包含时间信号的小型 JSON 文件,同时在文件上持有排他阻塞锁。成功时返回 TRUE,否则返回 FALSE。您的应用程序应正确处理 FALSE 响应。

所以现在我们在服务器上有一个小 JSON 文件,它表示数据库更改的最新时间。可以在使用很少资源的情况下经常轮询此文件。

您暗示要使用长轮询。在这种情况下,请查看 using server-sent events

您可以使用以下内容轮询 PHP 代码中的 signal.json

function getTimeSignal()
{
    $timeSignal = FALSE;
    if ($file = fopen('signal.json','r')) 
    {
        if (flock($file,LOCK_SH)) {
            $json = fread($file,filesize('signal.json'));
            flock($file,LOCK_UN); 
            $timeSignal = json_decode($json);
        }
        fclose($file);
    }
    return $timeSignal;
}

这将返回存储的时间信号,或 FALSE。通过使用文件锁定,在这两个函数中,您可以保证文件中始终存在有效的时间戳。

当客户端检索到一个新的时间信号时,它可以使用它以及它获得的前一个时间信号,向服务器询问在这两个信号之间发生的所有数据库更改。

此方法假定时间信号检查比时间信号更新要多得多。只有这样才值得使用这种方法。

我使用了一个文件来存储时间信号,但这不是唯一的选择。时间信号可以存储在存储器中。在几乎所有情况下,这都需要对 PHP 配置进行一些控制,考虑到您已经制定的所有约束,我不确定您是否有。

在此示例代码中,我使用了时间信号。这几乎意味着您的数据库也存储这些时间信号。情况可能并非如此。当然,您可以添加它们,但您也可以使用其他东西。我不知道该建议什么,但它可以作为最新聊天消息的索引,例如在聊天应用中。