如何设计将请求放入队列的后端


How to design the backend to put requests into a queue?

我想设计一个用户可以提交文件的系统。在他们提交文件后,我会用这些文件运行一些脚本。我想按顺序运行这些文件,所以我想维护一个请求队列。如何使用php实现这一点?有开源库吗?

谢谢!

我会使用数据库。。

  1. 当用户提交一个文件时,它会向数据库添加一个指向该文件在文件系统中位置的链接
  2. 您有一个运行的cron,它检查新提交并按顺序处理它们,完成后会在数据库中将其标记为已处理

我会使用Redis。Redis是一个超高速的键值存储;通常它的响应时间是两位数微秒。(10-99微秒)

Redis事务是原子性的(事务要么发生,要么不发生),您可以在不使用cron的情况下让作业不断运行。

要在PHP中使用Redis,您可以使用Predis。

一旦安装了redis,Predis被设置为使用您的脚本,在上传文件时,我会做这样的事情:

// 'hostname' and 'port' is the hostname and the port
// where Redis is installed and listening to.  
$client = new Predis'Client('tcp://hostname:port');
// assuming the path to the file is stored in $pathToFile
$client->lpush('queue:files', $pathToFile);

然后需要处理文件的脚本,只需要做一些类似的事情:

$client = new Predis'Client('tcp://hostname:port');
// assuming the path to the file is stored in $pathToFile
while(true){
    $pathToFile = $client->rpop('queue:files');
    if(!$pathToFile) {
        // list is empty, so move on. 
        continue;
    }
    // there was something in the list, do whatever you need to do with it.
    // if there's an exception or an error, you can always use break; or exit; to terminate the loop.
}

考虑到PHP往往会使用大量内存,所以我会显式地收集垃圾(通过gc_enable()gc_collect_cycles()unset()变量)。

或者,您可以使用类似supervisord的软件使该脚本只运行一次,一旦结束,就重新启动它。

一般来说,我不会使用数据库和cron来实现队列。这可能会导致严重的问题。

例如,假设您使用一个表作为队列。

在第一次运行中,脚本从数据库中提取一个作业并开始执行它的任务。

然后由于某种原因,您的脚本需要更长的时间才能运行,cron作业再次启动,现在您有两个脚本在同一个文件中工作。这可能没有任何后果,也可能产生严重后果。这取决于您的应用程序实际在做什么。

因此,除非您使用的是一个非常小的数据集,并且您确信您的cronjob将在上一个脚本运行之前完成,并且不会发生冲突,否则您应该没事。否则,请远离这种方法。

第三方库?太简单了,不需要一个完整的图书馆。如果你想浪费时间和资源,然后又不得不关心垃圾收集,那么你可以使用Redis(见AlanSavez的回答),而真正的解决方案是一开始就不把垃圾带入垃圾中

您的队列是一个文本文件。上传文件时,文件名会附加到队列中

$q= fopen('queue.txt','a');

'a'模式很重要。它会自动将写入指针移动到文件末尾以进行追加写入。但它之所以重要,是因为如果文件不存在,就会创建一个新文件

fwrite($q,"$filename'n");
fclose($q);

如果同时对该文件进行追加写入,则操作系统会无错误地仲裁冲突。无需文件锁定、协作多任务处理或事务处理

当处理队列的脚本开始运行时,它会将活动队列重命名为工作队列

if(!file_exists('q.txt')){
  if(!file_exists('queue.txt')){exit;}
  rename('queue.txt','q.txt');
  $q = fopen(`q.txt`,'r');
  while (($filename = fgets($q, 4096)) !== false) {
    process($filename);
  }
  fclose($q);
  unlink('q.txt');
}
else{
  echo 'Houston, we have a problem';
}

现在您可以了解为什么'a'模式很重要了。我们重命名队列,当下次上传时,会自动创建queue.txt

如果在重命名文件时写入该文件,操作系统将对其进行无错误的排序。重命名的速度如此之快,以至于同时写入的机会微乎其微。解决文件系统争用是操作系统的一个基本功能。无需文件锁定、协作多任务处理或事务处理

这是我多年来一直使用的一种防弹方法

用错误恢复例程替换Apollo 13报价。如果q.txt存在,则先前的处理未成功完成

这太容易了

因为它很简单,我们有足够的记忆力,因为我们效率很高:让我们玩得开心

让我们看看写入队列是否比AlanSavez的"超快"Redis(只有两位数的毫秒响应)更快

将文件名添加到队列的时间(以秒为单位)=0.000014537或14.5µS。比Redis的"超快"10-99 mS响应时间至少好100000%