Exemplo n.º 1
0
 /**
  * The primary loop for a worker which when called on an instance starts
  * the worker's life cycle.
  *
  * Queues are checked every $interval (seconds) for new jobs.
  */
 public function work()
 {
     $this->log('Starting worker <pop>' . $this . '</pop>', Logger::INFO);
     $this->updateProcLine('Worker: starting...');
     $this->startup();
     $this->log('Listening to queues: <pop>' . implode(', ', $this->queues) . '</pop>, with ' . ($this->blocking ? 'timeout blocking' : 'time interval') . ' <pop>' . $this->interval_string() . '</pop>', Logger::INFO);
     while (true) {
         if ($this->memoryExceeded()) {
             $this->log('Worker memory has been exceeded, aborting', Logger::CRITICAL);
             $this->shutdown();
             Event::fire(Event::WORKER_LOW_MEMORY, $this);
         }
         if (!$this->redis->sismember(self::redisKey(), $this->id) or $this->redis->hlen(self::redisKey($this)) == 0) {
             $this->log('Worker is not in list of workers or packet is corrupt, aborting', Logger::CRITICAL);
             $this->shutdown();
             Event::fire(Event::WORKER_CORRUPT, $this);
         }
         if ($this->shutdown) {
             $this->log('Shutting down worker <pop>' . $this . '</pop>', Logger::INFO);
             $this->updateProcLine('Worker: shutting down...');
             break;
         }
         if ($this->status == self::STATUS_PAUSED) {
             $this->log('Worker paused, trying again in ' . $this->interval_string(), Logger::INFO);
             $this->updateProcLine('Worker: paused');
             sleep($this->interval);
             continue;
         }
         $this->host->working($this);
         $this->redis->hmset(self::redisKey($this), 'memory', memory_get_usage());
         Event::fire(Event::WORKER_WORK, $this);
         if (!count($this->resolveQueues())) {
             $this->log('No queues found, waiting for ' . $this->interval_string(), Logger::INFO);
             sleep($this->interval);
             continue;
         }
         $this->queueDelayed();
         if ($this->blocking) {
             $this->log('Pop blocking with timeout of ' . $this->interval_string(), Logger::INFO);
             $this->updateProcLine('Worker: waiting for job on ' . implode(',', $this->queues) . ' with blocking timeout ' . $this->interval_string());
         } else {
             $this->updateProcLine('Worker: waiting for job on ' . implode(',', $this->queues) . ' with interval ' . $this->interval_string());
         }
         $job = \Resque::pop($this->resolveQueues(), $this->interval, $this->blocking);
         if (!$job instanceof Job) {
             if (!$this->blocking) {
                 $this->log('Sleeping for ' . $this->interval_string(), Logger::INFO);
                 sleep($this->interval);
             }
             continue;
         }
         $this->log('Found a job <pop>' . $job . '</pop>', Logger::NOTICE);
         $this->workingOn($job);
         Event::fire(Event::WORKER_FORK, array($this, $job));
         // Fork into another process
         $this->child = pcntl_fork();
         // Returning -1 means error in forking
         if ($this->child == -1) {
             Event::fire(Event::WORKER_FORK_ERROR, array($this, $job));
             $this->log('Unable to fork process, this is a fatal error, aborting worker', Logger::ALERT);
             $this->log('Re-queuing job <pop>' . $job . '</pop>', Logger::INFO);
             // Because it wasn't the job that failed the job is readded to the queue
             // so that in can be tried again at a later time
             $job->queue();
             $this->shutdown();
             // In parent if $pid > 0 since pcntl_fork returns process id of child
         } elseif ($this->child > 0) {
             Event::fire(Event::WORKER_FORK_PARENT, array($this, $job, $this->child));
             $this->log('Forked process to run job on pid:' . $this->child, Logger::DEBUG);
             $this->updateProcLine('Worker: forked ' . $this->child . ' at ' . strftime('%F %T'));
             // Set the PID in redis
             $this->redis->hset(self::redisKey($this), 'job_pid', $this->child);
             // Wait until the child process finishes before continuing
             pcntl_wait($status);
             if (!pcntl_wifexited($status) or ($exitStatus = pcntl_wexitstatus($status)) !== 0) {
                 if ($this->job->getStatus() == Job::STATUS_FAILED) {
                     $this->log('Job ' . $job . ' failed: "' . $job->failError() . '" in ' . $this->job->execTimeStr(), Logger::ERROR);
                 } else {
                     $this->log('Job ' . $job . ' exited with code ' . $exitStatus, Logger::ERROR);
                     $this->job->fail(new Exception\Dirty($exitStatus));
                 }
             }
         } else {
             Event::fire(Event::WORKER_FORK_CHILD, array($this, $job, getmypid()));
             $this->log('Running job <pop>' . $job . '</pop>', Logger::INFO);
             $this->updateProcLine('Job: processing ' . $job->getQueue() . '#' . $job->getId() . ' since ' . strftime('%F %T'));
             $this->perform($job);
             exit(0);
         }
         $this->child = null;
         $this->doneWorking();
     }
 }