【问题标题】:How to write multiprocess queues in Laravel 4.2 (having supervisord configured)?如何在 Laravel 4.2 中编写多进程队列(配置了 supervisord)?
【发布时间】:2019-04-01 13:31:51
【问题描述】:

我有一个网站,用户可以在其中以 CSV 格式导出报告。当他们单击“导出”按钮时,我在我的数据库表中添加了一个新行,表明有一个新的报告生成请求。

我想为 2 个进程设置数据库访问权限,因为 1 个还不够。我当前的supervisord配置如下:

    [program:csv_export]
    command=php /var/www/mywebsite.com/artisan queue:listen --tries=1 --timeout=3000 --queue=csv_export
    numprocs=2
    process_name=csv_export_%(process_num)02d
    directory=/var/www/mywebsite.com/
    stdout_logfile=/var/log/mywebsite/csv_export.log
    autostart=true
    autorestart=true
    stopsignal=KILL
    loglevel=debug
    redirect_stderr=true

我的队列fire() 代码如下所示:

public function fire($job, $data){
    $reports = \CSVReport::where('status', '=', CSVReport::CSVNEW)->take(1)->get();

    foreach ($reports as $key => $value){
        // prepare data and file
        $value->status = 'done';
        $value->save();
    }

    $job->delete();
}

我使用的是 Laravel 4.2,还不能升级。我想避免两个进程访问数据库中的同一表行的情况。如何在我的队列类中避免这种情况?

【问题讨论】:

  • 队列自动逐一处理作业:laravel.com/docs/4.2/queues
  • 我猜你可能会尝试重写队列管理器以根据进程名称在队列表中仅获取奇数行或偶数行
  • @tohhy of caurse no,但每个进程都不会看到属于其他进程的作业(奇数/偶数)
  • @tohhy 忘记了 2 supervisord conf。关于偶数/奇数方法。您不能将 CSVReport ID 作为属性传递给 Job protected $csvID; 并在构造函数中传递 ID 值,然后使用 $reports = \CSVReport::find($this->csvID); 选择该 CSVReport 吗?
  • 您实现它的方式就像客户要求 CSVReport ID 100 导致的 Job 100 可能会处理 CSV Report ID 95 而不是 100。传递 CSVReport ID 将确保 Job 100 只会处理CSV 报告 100。

标签: php laravel supervisord laravel-4.2 laravel-queue


【解决方案1】:

假设您将您的作业推送到队列中,例如:

$csv = new CSVReport;
// $csv->... = ...;
$csv->save();
Queue::push('ExportCSV', array('csvID' => $csv->id));

Job fire 方法使用$data 参数传递特定值以确保 Job 执行特定操作。现在在 ExportCSV 作业中,在 fire 方法中:

public function fire($job, $data){
    $report = \CSVReport::find($data['csvID']);

    // prepare data and file
    $report->status = 'done';
    $report->save();

    $job->delete();
}

这样每个 Job 都会在他的 $data 中序列化 CSVReport ID,并且在 supervisord 执行它的时候,Job 只会在数据库中处理他特定的 CVSReport ID。

如果有任何情况可以在队列外处理它们,您仍然可以检查状态:

public function fire($job, $data){
    $report = \CSVReport::where('status', CSVReport::CSVNEW)->where('id', $data['csvID'])->first();

    // prepare data and file
    if($report) {
        $report->status = 'done';
        $report->save();
    }

    $job->delete();
}

【讨论】:

  • 我想问:两个进程不会同时处理同一个csvID的保证是什么?有时,当两个进程同时访问数据库并获得相同的 id 时,可能会发生这种情况。我错了吗?我只是不明白它是如何工作的机制。
  • @tohhy 当您调用$csv->save(); 时,MySQL 或数据库系统将自动递增的id 分配给csv_reports 表中的记录。然后你打电话给Queue::push('ExportCSV', array('csvID' => $csv->id));,它将这个ID永久分配给Job。同时保存的任何其他 CSVReport 将获得与其他人不同的id,因为数据库自动增量永远不会分配相同的id 两次,即使事务未能完成。这样,确保只有 1 个 SINGLE JOB 可以访问只有 1 个 SINGLE CSVReport 记录。
  • @tohhy 您使用的是哪个数据库系统? MySQL、SQL 服务器?甲骨文? NoSQL 类型? MySQL、SQL Server 和 Oracle 应该没问题
  • 我正在使用 MariaDB。
  • 谢谢,我已经在我的系统中实现了这个,一切都很好。 :)
猜你喜欢
  • 2015-08-14
  • 2020-04-22
  • 2017-12-26
  • 1970-01-01
  • 2017-11-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-09-20
相关资源
最近更新 更多