【问题标题】:How to keep Laravel Queue system running on server如何保持 Laravel Queue 系统在服务器上运行
【发布时间】:2015-04-21 18:41:53
【问题描述】:

我最近设置了一个 Laravel 队列系统。基础是 cronjob 调用将作业添加到队列的命令并调用发送电子邮件的第二个命令。

当我 ssh 进入我的服务器并运行 php artisan queue:listen 时系统工作,但如果我关闭我的终端,监听器将关闭,作业堆积起来并排在队列中,直到我 ssh 重新进入并再次运行监听。

让队列系统在后台运行而不需要通过 ssh 保持连接打开的最佳方法是什么?

我尝试运行php artisan queue:work --daemon,它完成了队列中的作业,但是当我关闭终端时,它关闭了连接和后台进程。

【问题讨论】:

    标签: laravel crontab laravel-artisan


    【解决方案1】:

    如果您开始在屏幕内收听会怎样?见这里:http://aperiodic.net/screen/quick_reference 然后,即使您注销,屏幕仍将处于活动状态并正在运行。不知道为什么守护进程不起作用。

    【讨论】:

      【解决方案2】:

      跑步

      nohup php artisan queue:work --daemon &
      

      会在你注销时阻止命令退出。

      尾随的和号 (&) 导致进程在后台启动,因此您可以继续使用 shell 而不必等到脚本完成。

      nohup

      nohup - 运行不受挂断影响的命令,输出到非 tty

      这会将信息输出到您运行命令的目录中名为 nohup.out 的文件中。如果您对输出不感兴趣,您可以将 stdout 和 stderr 重定向到 /dev/null,或者类似地您可以将其输出到您的普通 laravel 日志中。例如

      nohup php artisan queue:work --daemon > /dev/null 2>&1 &
      
      nohup php artisan queue:work --daemon > app/storage/logs/laravel.log &
      

      但您还应该使用Supervisord 之类的东西来确保服务保持运行并在崩溃/失败后重新启动。

      【讨论】:

      • 啊,太棒了!我认为这将是公认的答案!我很快就能测试了。谢谢。
      • 太棒了..这让我很开心
      • 首先我需要:stackoverflow.com/a/29292637/470749 然后nohup php artisan queue:work --daemon > storage/logs/laravel.log & 为我工作。注意:如果你想杀死 nohup 守护进程,你需要首先通过运行ps -ef |grep artisan 之类的东西来发现它的 PID。然后你可以运行kill [pid]stackoverflow.com/q/17385794/470749
      • 这是一个糟糕的解决方案,因为一旦你发出 queue:reset 工人死亡并且你没有重新启动机制,只需使用 supervisord,产生 2 个工人,当你量大。附带说明一下,每次更改代码时都需要重新生成新的工作人员。
      • @z900collector 答案明确表示你应该使用像 Supervisord 这样的东西,并且一直这样做。
      【解决方案3】:

      命令

      nohup php artisan queue:work --daemon &
      

      是正确的,它会在关闭 SSH 连接后允许进程继续;然而,这只是一个短期的解决办法。一旦您的服务器重新启动或任何问题导致进程停止,您将需要返回并再次运行该命令。当这种情况发生时,你永远不知道。它可能发生在星期五晚上,因此最好实施长期解决方案。

      我最终切换到了 Supervisord,这可以像安装在 Ubuntu 上一样简单

      sudo apt-get install supervisor 
      

      对于 AWS-AMI 或 RedHat 用户,您可以按照我在此问题中概述的说明进行操作:

      Setting up Supervisord on a AWS AMI Linux Server

      【讨论】:

      • 你好,你能帮我解决我的问题吗?我正在使用 supervisord 并且作业已执行,但无法创建文件 stackoverflow.com/questions/47715537/…
      • 那你为什么不接受主管建议的答案?
      • 该答案仅适用于 Ubuntu 用户,我的答案链接到一个单独的问题,该问题涉及如何在基于 RedHat 的发行版上进行设置。另外,我回答了 Dev 15 2016,其他仅针对 Ubuntu 用户的答案出现在 2017 年 6 月。
      • 不要迂腐,但@deewwald 可能指的是the answer here you said you would likely accept - AFAICT 这是发布的第一个(有意义的)答案,它建议使用主管,它并不特定于任何操作系统。跨度>
      • 啊,我同意这个答案是有意义的,并且回答了这个问题,但这确实是一个糟糕的解决方案。为了使队列进程可靠,应该集成某种排序或进程监视器。 laravel 社区似乎更倾向于 Supervisor,但我也看到 Monit 的使用取得了成功。
      【解决方案4】:

      你应该使用 linux supervisor

      安装很简单,在 Ubuntu 上我可以使用以下命令安装它:

      apt-get install supervisor
      

      Supervisor 配置文件位于 /etc/supervisor/conf.d 目录下。

      [program:email-queue]
      process_name=%(program_name)s_%(process_num)02d
      command=php /var/www/laravel-example/artisan queue:work redis --queue=emailqueue --sleep=3 --tries=3
      autostart=true
      autorestart=true
      user=forge
      numprocs=2
      redirect_stderr=true
      stdout_logfile=/var/www/laravel-example//storage/logs/supervisord.log
      

      您应该为每个进程创建一个新的进程配置文件。使用此配置,侦听器将重试每个作业 3 次。如果侦听器失败或系统重新启动,Supervisor 也会重新启动侦听器。

      【讨论】:

      【解决方案5】:

      来自https://gist.github.com/ivanvermeyen/b72061c5d70c61e86875

      <?php
      
      namespace App\Console\Commands;
      
      use Illuminate\Console\Command;
      
      class EnsureQueueListenerIsRunning extends Command
      {
          /**
           * The name and signature of the console command.
           *
           * @var string
           */
          protected $signature = 'queue:checkup';
      
          /**
           * The console command description.
           *
           * @var string
           */
          protected $description = 'Ensure that the queue listener is running.';
      
          /**
           * Create a new command instance.
           */
          public function __construct()
          {
              parent::__construct();
          }
      
          /**
           * Execute the console command.
           *
           * @return void
           */
          public function handle()
          {
              if ( ! $this->isQueueListenerRunning()) {
                  $this->comment('Queue listener is being started.');
                  $pid = $this->startQueueListener();
                  $this->saveQueueListenerPID($pid);
              }
      
              $this->comment('Queue listener is running.');
          }
      
          /**
           * Check if the queue listener is running.
           *
           * @return bool
           */
          private function isQueueListenerRunning()
          {
              if ( ! $pid = $this->getLastQueueListenerPID()) {
                  return false;
              }
      
              $process = exec("ps -p $pid -opid=,cmd=");
              //$processIsQueueListener = str_contains($process, 'queue:listen'); // 5.1
              $processIsQueueListener = ! empty($process); // 5.6 - see comments
      
              return $processIsQueueListener;
          }
      
          /**
           * Get any existing queue listener PID.
           *
           * @return bool|string
           */
          private function getLastQueueListenerPID()
          {
              if ( ! file_exists(__DIR__ . '/queue.pid')) {
                  return false;
              }
      
              return file_get_contents(__DIR__ . '/queue.pid');
          }
      
          /**
           * Save the queue listener PID to a file.
           *
           * @param $pid
           *
           * @return void
           */
          private function saveQueueListenerPID($pid)
          {
              file_put_contents(__DIR__ . '/queue.pid', $pid);
          }
      
          /**
           * Start the queue listener.
           *
           * @return int
           */
          private function startQueueListener()
          {
              //$command = 'php-cli ' . base_path() . '/artisan queue:listen --timeout=60 --sleep=5 --tries=3 > /dev/null & echo $!'; // 5.1
              $command = 'php-cli ' . base_path() . '/artisan queue:work --timeout=60 --sleep=5 --tries=3 > /dev/null & echo $!'; // 5.6 - see comments
              $pid = exec($command);
      
              return $pid;
          }
      }
      

      【讨论】:

      • 感谢分享,这是确保队列运行的独特方式!很好,因为不需要安装新的依赖项,只需要一个 CRONTAB 的句柄。任何权限设置问题?
      • 没有权限问题。只需要小心php版本和路径,有时它们与shell不同。另外 exec() 经常在共享主机上关闭..
      • 已针对 5.6 更新,请参阅 5.1 链接上的 cmets
      • 这里使用queue:work 表示没有监听任何代码更改,或者至少需要手动重启进程。但在代码中我没有看到任何重新启动..有什么想法吗?
      • 这真的拯救了我的网站!别人推荐的主管我没空,这个方法效果很好。没有任何问题。我确实必须针对我的环境稍微更改命令。
      【解决方案6】:

      您可以使用monit 工具。它非常小巧,可用于任何类型的流程管理和监控。

      this link下载二进制包后,你可以将它解压到你系统上的一个文件夹中,然后从包中复制两个文件到你的系统中进行安装:

      cd /path/to/monit/folder
      cp ./bin/monit /usr/sbin/monit
      cp ./conf/monitrc /etc/monitrc  
      

      现在根据您的需要编辑/etc/monitrc (reference doc)。然后创建一个init control file 以在启动时启用监控。现在像这样开始监控:

      initctl reload-configuration
      start monit
      

      【讨论】:

        【解决方案7】:

        对于那些已经在他们的生产环境中运行 NodeJS 的人。我使用 PM2 来管理应用进程。

        # install
        npm install -g pm2
        
        # in project dir with your CI or dev setup tool 
        # --name gives task a name so that you can later manage it
        # -- delimits arguments that get passed to the script
        pm2 start artisan --interpreter php --name queue-worker -- queue:work --daemon
        

        我在开发和设置 NodeJS 时使用 Vagrant,而这个过程仅使用内联 vagrant 脚本。

        当您在开发中使用 PM2 时,您可以使用众多观察者之一来管理重启。当您进行更改时,只需运行pm2 restart queue-worker。在生产中我不推荐这种方法,而是选择可以遵循此过程的构建工具。

        # 1. stop pm task to ensure that no unexpected behaviour occurs during build
        pm2 stop queue-worker
        # 2. do your build tasks
        ...
        # 3. restart queue so that it loads the new code
        pm2 restart queue-worker
        

        【讨论】:

          【解决方案8】:

          使用 pm2

          我使用pm2(高级,Node.js 的生产流程管理器)运行 JS 脚本,这是我唯一运行的脚本。但现在我又多了一个进程可以继续运行。

          我创建了process.yml 以使用单个命令运行两者。 检查第一个将运行php artisan queue: listen

          # process.yml at /var/www/ which is root dir of the project
          apps:
            # Run php artisan queue:listen to execute queue job
            - script    : 'artisan'
              name      : 'artisan-queue-listen'
              cwd       : '/var/www/'
              args      : 'queue:listen' # or queue:work
              interpreter : 'php'
          
            # same way add any other script if any.
          

          现在运行:

          > sudo pm2 start process.yml
          

          Check more options and feature of pm2

          【讨论】:

            【解决方案9】:

            由于这是一个特定于 Laravel 的问题,我想我会建议一个特定于 Lravel 的答案。由于您已经在此服务器上使用了 cronjobs,我建议您将 shell 命令设置为重复的 cronjob,以始终验证 worker 是否正在运行。您可以将 shell 命令设置为通过服务器上的 cron 本地运行,或者您可以使用 Laravel 控制台内核来管理命令并添加逻辑,例如检查您是否已经有一个工作人员正在运行,如果没有,请继续并重新启动它。

            根据您需要运行命令的频率,您可以不频繁地执行此操作,每周一次,甚至每分钟一次。这将使您能够确保您的工作人员持续运行,而不必为您的服务器(例如 Supervisor)增加任何开销。如果您信任像 supervisor 这样的 3rd 方包,则授予权限是可以的,但如果您可以避免依赖它,您可能需要考虑这种方法。

            使用它来做你想做的事情的一个例子是有一个每小时运行的 cronjob。它会在自定义 Laravel 控制台命令中按顺序执行以下命令:

            \Artisan::call('queue:restart');

            \Artisan::call('queue:work --daemon');

            请注意,这适用于旧版本的 Laravel(最高 5.3),但我尚未在新版本上进行测试。

            【讨论】:

            • 虽然这是一种选择并且可行,但在最坏的情况下,这将导致排队任务停机一小时。此外,这看起来每次 cron 运行时都会创建一个新进程,如果是这样,最终你会耗尽内存。
            • 一个小时的停机时间?我的意思是这两个工匠命令应该在 cronjob 中按顺序运行,而不是相隔一个小时。我将更新原始答案以反映这一点。这不会导致内存问题,因为重启命令会杀死前一个进程。
            • 关于潜在的停机时间,我们在服务器上遇到了一个问题,即工匠守护进程工作人员最终会因未知原因被杀死并且无法存活。监督者是确保它在死亡时复活的唯一方法。
            • 上面的代码也应该可以工作。您可以根据需要随时调用这些命令,每分钟调用一次。所以我不明白关于使用这种方法有一个小时的停机时间的评论。您可以控制检查和重新启动守护程序的频率。发布这篇文章的全部目的只是为了举例说明如何仅使用 Laravel 来做到这一点。肯定还有其他方法可以做到这一点。但是这种方式不依赖于安装外部包。
            • 我认为大多数人不介意安装进程监视器,但其他人可能不想授予对他们无法控制的外部包的访问级别。这是个人喜好。如果您不介意在服务器上进行这种类型的安装,服务监视器可能会更干净,但是这种方法无需任何额外的外部依赖即可实现相同的效果,并且应该与平台无关。但两者都有优点和缺点。
            【解决方案10】:

            我只是使用了php artisan queue:work --tries=3 &amp;,它使进程在后台运行。 但它有时会停止。 我不知道为什么会这样

            编辑

            我通过使用主管解决了这个问题。 放置一个运行这个 php 脚本的主管脚本,它会在每次服务器运行时运行

            【讨论】:

            • 它会停止,因为您的服务器有时会重新启动。
            【解决方案11】:

            最好的方法是 PM2(高级,Node.js 的生产流程管理器),您可以监控队列并查看其日志。

            在您的项目目录中使用以下命令,运行队列工作者:

            pm2 start artisan --name laravel-worker --interpreter php -- queue:work --daemon
            

            【讨论】:

              【解决方案12】:

              1)sudo apt install supervisor

              sudo apt-get install supervisor
              

              2)cd /etc/supervisor/conf.d 3)在里面创建新文件

              sudo vim queue-worker.conf
              

              文件内容

              [program:email-queue]
              process_name=%(program_name)s_%(process_num)02d
              command=php /var/www/html/laravelproject/artisan queue:work
              autostart=true
              autorestart=true
              user=root
              numprocs=2
              redirect_stderr=true
              stdout_logfile=/var/www/html/laravelproject/storage/logs/supervisord.log
              

              4)sudo supervisorctl reread

              运行此命令时获取输出 queue-worker:available

              5)sudo supervisorctl update

              运行此命令时获取输出 queue-worker: added 进程组

              其他命令

              1)sudo supervisorctl reload

              当运行这个命令得到输出Restarted supervisord

              2)sudo service supervisor restart

              【讨论】:

              • 是的,它对我有用,我也尝试另一种方法,我制定了一个时间表,每 5 分钟开始我的队列工作工匠,这也是工作
              • 你在哪个 Linux 发行版上使用它?
              • 为我工作(服务器 Ubuntu 20.04 / php 7.4,Laravel 8,radis)。但我将 user=root 更改为 user=www-data 。命令行:command=php /var/www/path-to-project/artisan queue:work --sleep=3 --tries=3 --max-time=3600
              【解决方案13】:

              安装主管

              sudo apt-get install supervisor
              

              配置主管

              第一步:进入/etc/supervisor/conf.d目录

              cd /etc/supervisor/conf.d
              

              第 2 步:创建一个工作文件 laravel-worker.conf 将监听队列

              sudo nano laravel-worker.conf
              

              *注意:现在假设你的 laravel 应用在 /var/www/html 目录中

              project folder is : /var/www/html/LaravelApp
              

              第 3 步:将以下代码粘贴到 laravel-worker.conf 中并保存文件

              [program:laravel-worker]
              process_name=%(program_name)s_%(process_num)02d
              command=php /var/www/html/LaravelApp/artisan queue:listen redis --queue=default --sleep=3 --tries=3 
              autostart=true
              autorestart=true
              user=root
              numprocs=8
              redirect_stderr=true
              stdout_logfile= /var/www/html/LaravelApp/storage/logs/worker.log
              

              *注意:这里假设您使用redis进行队列连接

              在.env文件中QUEUE_CONNECTION=redis

              command=php /var/www/html/LaravelApp/artisan queue:listen redis
              

              如果您使用其他连接,那么一般语法将是:

              command= php [project_folder_path]/artisan queue:listen [connection_name]
              

              [connection_name] 可以是 syncdatabasebeanstalkdsqsredis 中的任何一个

              第 4 步:创建一个工作文件 laravel-schedule.conf,它将在 每 1 分钟(60 秒) 运行 artisan schedule:run 命令(*您可以更改它根据您的要求)

              [program:laravel-schedule]
              process_name=%(program_name)s_%(process_num)02d
              command=/bin/bash -c 'while true; do date && php /var/www/html/LaravelApp/artisan schedule:run; sleep 60; done'
              autostart=true
              autorestart=true
              numprocs=1
              stdout_logfile=/dev/stdout
              stdout_logfile_maxbytes=0
              

              第 5 步:启动主管:运行以下命令

              sudo supervisorctl reread
              
              sudo supervisorctl update
              
              sudo supervisorctl start all
              

              *注意:每当您更改任何配置 .conf 文件时,请运行 Step 5

              的上述命令

              额外有用的信息:

              • 停止所有 supervisorctl 进程:sudo supervisorctl stop all
              • 重启所有supervisorctl进程:sudo supervisorctl restart all

              有用的链接:

              https://laravel.com/docs/5.8/queues#running-the-queue-worker

              http://supervisord.org/index.html

              【讨论】:

                【解决方案14】:

                CentOS7

                yum install supervisor
                

                然后在 /etc/supervisord.d/filename.ini 中创建一个文件 有内容

                [program:laravel-worker]
                command=/usr/bin/php /home/appuser/public_html/artisan queue:listen
                process_name=%(program_name)s_%(process_num)02d
                numprocs=5
                priority=999
                autostart=true
                autorestart=true
                startsecs=1
                startretries=3
                user=appuser
                redirect_stderr=true
                stdout_logfile=/path/logpath/artisan.log
                

                然后使用启动supervisord服务

                systemctl restart supervisord
                

                使 supervisord 服务在启动时运行

                systemctl enable supervisord
                

                检查服务是否正在使用

                ps aux | grep artisan
                

                如果设置正确,您应该会看到该进程正在运行。类似于下面的输出。

                [root@server ~]# ps aux | grep artisan
                appuser 17444  0.1  0.8 378656 31068 ?        S    12:43   0:05 /usr/bin/php /home/appuser/public_html/artisan queue:listen
                

                【讨论】:

                  【解决方案15】:

                  【讨论】:

                    【解决方案16】:

                    对于将 systemd 作为初始化服务的系统,您可以使用以下服务,使其适应您的项目(在 /etc/systemd/system/queue-handler.service 上创建):

                    [Unit]
                    Description = Queue Handler - Project
                    After = network-online.target, mysql.service
                    
                    [Service]
                    User = www-data
                    Type = simple
                    WorkingDirectory=/var/www/project
                    ExecStart = /usr/bin/php /var/www/project/artisan queue:work --tries=3
                    Restart = on-failure
                    RestartSec=5s
                    RestartPreventExitStatus = 255
                    
                    [Install]
                    WantedBy = multi-user.target
                    

                    重新加载配置并在启动时启用它:

                    $ systemctl enable queue-handler.service
                    $ systemctl daemon-reload
                    

                    【讨论】:

                      【解决方案17】:

                      我在没有任何服务监视器或第三方软件的情况下取得了结果。该解决方案运行良好,但我不确定这是否是最好的方法。

                      解决方案

                      只需在您的函数中按以下方式运行 cli 命令即可。

                      use Illuminate\Console\Command;
                      
                      public function callQueue()
                      {
                              $restart = 'php-cli ' . base_path() . '/artisan queue:restart > /dev/null & echo $!'; 
                              $work = 'php-cli ' . base_path() . '/artisan queue:work --timeout=0 --sleep=5 --tries=3 > /dev/null & echo $!';
                              exec($restart);
                              exec($work);
                      }
                      
                      $job = (new jobName())->delay(Carbon::now()->addSeconds(5));
                      dispatch($job);
                      

                      原因

                      我使用这两个命令的原因是因为与$restart 关联的命令根据this answer 中的评论防止出现任何内存问题 并且与$work关联的命令确保该命令在作业之前成功执行。

                      【讨论】:

                      • 这个解决方案对我不起作用。当您在queue:work 上执行&gt; /dev/null 时,队列根本不会执行。在dispatch(...) 之前或之后调用函数都没有关系。如果我删除 &gt; /dev/null 则它可以工作,但是在尝试发送电子邮件时会出现很大延迟。
                      【解决方案18】:

                      你可以运行命令行:

                      php artisan queue:listen --queue=queue_name --tries=1 --memory=128 --timeout=300 >>  storage/logs/queue_log.log &
                      

                      检查进程运行:

                      ps aux | grep php
                      

                      【讨论】:

                        【解决方案19】:

                        在 Ubuntu 上使用 AWS SQS 连接

                        安装主管

                        sudo apt-get install supervisor
                        

                        配置主管

                        第 1 步:转到 /etc/supervisor/conf.d 目录

                        cd /etc/supervisor/conf.d
                        

                        第二步:创建一个工作文件 laravel-worker.conf 来监听队列

                        [program:laravel-worker]
                        process_name=%(program_name)s_%(process_num)02d
                        command=/opt/bitnami/php/bin/php /opt/bitnami/nginx/html/website/queue:work sqs
                        autostart=true
                        autorestart=true
                        user=root
                        numprocs=2
                        redirect_stderr=true
                        startsecs=0
                        stdout_logfile=/opt/bitnami/nginx/html/website/storage/logs/supervisord.log
                        

                        4)sudo supervisorctl reread

                        运行此命令时获取输出 queue-worker:available

                        5)sudo supervisorctl update

                        运行此命令时获取输出 queue-worker:add 进程组

                        其他命令

                        1)sudo supervisorctl reload

                        当运行这个命令得到输出Restarted supervisord

                        2)sudo service supervisor restart

                        3)sudo supervisorctl status

                        查看进程状态

                        【讨论】:

                        • 已经有很多答案解释了如何使用supervisord。这个答案似乎没有添加任何内容。
                        猜你喜欢
                        • 1970-01-01
                        • 2013-01-26
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 1970-01-01
                        • 2016-03-25
                        • 1970-01-01
                        • 2015-11-24
                        相关资源
                        最近更新 更多