/** * Run to start workers */ public function run() { if (empty($this->queues)) { die('Set queues var containing the list of queues to work.' . PHP_EOL); } Resque::setBackend($this->redisBackend, $this->database, $this->password); if ($this->count > 1) { for ($i = 0; $i < $this->count; ++$i) { $pid = pcntl_fork(); if ($pid == -1) { die('Could not fork worker ' . $i . PHP_EOL); } elseif (!$pid) { // Child, start the worker $this->_startWorker(); break; } } } else { if (!empty($this->pidFile)) { file_put_contents($this->pidFile, getmypid()) or die('Could not write PID information to ' . $PIDFILE . PHP_EOL); } // Start a single worker $this->_startWorker(); } }
/** * Push a new job onto the queue * * @param string $job The job class * @param mixed $data The job data * @param string $queue The queue to add the job to * @return Job job instance */ public function push($job, array $data = null, $queue = null) { if (false !== ($delay = \Resque::getConfig('default.jobs.delay', false))) { return $this->later($delay, $job, $data, $queue); } return Job::create($this->getQueue($queue), $job, $data); }
public function testAfterEnqueueEventCallbackFires() { $callback = 'afterEnqueueEventCallback'; $event = 'afterEnqueue'; Event::listen($event, array($this, $callback)); Resque::enqueue('jobs', TestJob::className(), array('somevar')); $this->assertContains($callback, $this->callbacksHit, $event . ' callback (' . $callback . ') was not called'); }
/** * Find the next available job from the specified queue and return an * instance of Job for it. * * @param string $queue The name of the queue to check for a job in. * @return null|object Null when there aren't any waiting jobs, instance of Job when a job was found. */ public static function reserve($queue = '*') { $payload = Resque::pop($queue); if (!is_array($payload)) { return false; } return new Job($queue, $payload); }
/** * Schedule all of the delayed jobs for a given timestamp. * * Searches for all items for a given timestamp, pulls them off the list of * delayed jobs and pushes them across to Resque. * * @param DateTime|int $timestamp Search for any items up to this timestamp to schedule. */ public function enqueueDelayedItemsForTimestamp($timestamp) { $item = null; while ($item = Scheduler::nextItemForTimestamp($timestamp)) { $this->log('queueing ' . $item['class'] . ' in ' . $item['queue'] . ' [delayed]'); if (!empty($item['args'])) { $item['args'] = reset($item['args']); } Event::trigger('beforeDelayedEnqueue', array('queue' => $item['queue'], 'class' => $item['class'], 'args' => $item['args'])); Resque::enqueue($item['queue'], $item['class'], $item['args']); } }
/** * Initialize a failed job class and save it (where appropriate). * * @param object $payload Object containing details of the failed job. * @param object $exception Instance of the exception that was thrown by the failed job. * @param object $worker Instance of \Resque\Worker that received the job. * @param string $queue The name of the queue the job was fetched from. */ public function __construct($payload, $exception, $worker, $queue) { $data = new \stdClass(); $data->failed_at = strftime('%a %b %d %H:%M:%S %Z %Y'); $data->payload = $payload; $data->exception = get_class($exception); $data->error = $exception->getMessage(); $data->backtrace = explode("\n", $exception->getTraceAsString()); $data->worker = (string) $worker; $data->queue = $queue; $data = json_encode($data); Resque::redis()->rpush('failed', $data); }
public function testRecreatedJobWithTrackingStillTracksStatus() { $originalToken = Resque::enqueue('jobs', TestJob::className(), null, true); $job = $this->worker->reserve(); // Mark this job as being worked on to ensure that the new status is still // waiting. $this->worker->workingOn($job); // Now recreate it $newToken = $job->recreate(); // Make sure we've got a new job returned $this->assertNotEquals($originalToken, $newToken); // Now check the status of the new job $newJob = Job::reserve('jobs'); $this->assertEquals(Status::STATUS_WAITING, $newJob->getStatus()); }
/** * Seperate the job from the worker via pcntl_fork * * @param Job $job */ public function perform(Job $job) { $this->child = Resque::fork(); // Forked and we're the child. Run the job. if ($this->child === 0) { parent::perform($job); exit(0); } // Parent process, sit and wait if ($this->child > 0) { $status = 'Forked ' . $this->child . ' at ' . strftime('%F %T'); $this->worker->updateProcLine($status); $this->worker->log($status); // Wait until the child process finishes before continuing pcntl_wait($status); $exitStatus = pcntl_wexitstatus($status); if ($exitStatus !== 0) { $job->fail(new Job\DirtyExitException('Job exited with exit code ' . $exitStatus)); } } $this->child = null; }
/** * Return an object describing the job this worker is currently working on. * * @return object Object with details of current job. */ public function job() { $job = Resque::redis()->get('worker:' . $this); if (!$job) { return array(); } else { return json_decode($job, true); } }
public function testJobWithNamespace() { Redis::prefix('php'); $queue = 'jobs'; $payload = array('another_value'); Resque::enqueue($queue, JobWithTearDown::className(), $payload); $this->assertEquals(Resque::queues(), array('jobs')); $this->assertEquals(Resque::size($queue), 1); Redis::prefix('resque'); $this->assertEquals(Resque::size($queue), 0); }
/** * Pop a job off the delayed queue for a given timestamp. * * @param DateTime|int $timestamp Instance of DateTime or UNIX timestamp. * * @return array Matching job at timestamp. */ public static function nextItemForTimestamp($timestamp) { $timestamp = self::getTimestamp($timestamp); $key = 'delayed:' . $timestamp; $item = json_decode(Resque::redis()->lpop($key), true); self::cleanupTimestamp($key, $timestamp); return $item; }
<?php require_once __DIR__ . '/../autoload.php'; use resque\Resque; if (empty($argv[1])) { die('Specify the name of a job to add. e.g, php queue.php PHP_Job'); } date_default_timezone_set('GMT'); // Resque::setBackend('127.0.0.1:6379', 0, 'your password'); Resque::setBackend('127.0.0.1:6379'); $args = array('time' => time(), 'array' => array('test' => 'test')); $jobId = Resque::enqueue('default', $argv[1], $args, true); echo "Queued job " . $jobId . "\n\n";
/** * Look for any jobs which are running but the worker is dead. * Meaning that they are also not running but left in limbo * * This is a form of garbage collection to handle cases where the * server may have been killed and the workers did not die gracefully * and therefore leave state information in Redis. * * @param array $queues list of queues to check */ public static function cleanup(array $queues = array('*')) { $cleaned = array('zombie' => 0, 'processed' => 0); $redis = Redis::instance(); if (in_array('*', $queues)) { $queues = (array) $redis->smembers(Queue::redisKey()); sort($queues); } $workers = $redis->smembers(Worker::redisKey()); foreach ($queues as $queue) { $jobs = $redis->zrangebyscore(Queue::redisKey($queue, 'running'), 0, time()); foreach ($jobs as $payload) { $job = self::loadPayload($queue, $payload); $packet = $job->getPacket(); if (!in_array($packet['worker'], $workers)) { $job->fail(new Exception\Zombie()); $cleaned['zombie']++; } } $cleaned['processed'] = $redis->zremrangebyscore(Queue::redisKey($queue, 'processed'), 0, time() - \Resque::getConfig('default.expiry_time', \Resque::DEFAULT_EXPIRY_TIME)); } return $cleaned; }
/** * Find the next available job from the specified queues using blocking list pop * and return an instance of Job for it. * * @param array $queues * @param int $timeout * @return null|object Null when there aren't any waiting jobs, instance of Job when a job was found. */ public static function reserveBlocking(array $queues, $timeout = null) { $item = Resque::blpop($queues, $timeout); if (!is_array($item)) { return false; } return new Job($item['queue'], $item['payload']); }
/** * Return Redis * * @return object Redis instance */ public function redis() { return Resque::redis(); }
public function testWorkerErasesItsStatsWhenShutdown() { Resque::enqueue('jobs', TestJob::className()); Resque::enqueue('jobs', '\\resque\\test\\job\\InvalidJob'); $worker = new Worker('jobs'); $worker->work(0); $worker->work(0); $this->assertEquals(0, $worker->getStat('processed')); $this->assertEquals(0, $worker->getStat('failed')); }
<?php require_once __DIR__ . '/../autoload.php'; use resque\Resque; use resque\core\job\Status; if (empty($argv[1])) { die('Specify the ID of a job to monitor the status of.'); } date_default_timezone_set('GMT'); Resque::setBackend('127.0.0.1:6379'); $status = new Status($argv[1]); if (!$status->isTracking()) { die("Resque is not tracking the status of this job.\n"); } echo "Tracking status of " . $argv[1] . ". Press [break] to stop.\n\n"; while (true) { fwrite(STDOUT, "Status of " . $argv[1] . " is: " . $status->get() . "\n"); sleep(1); }
/** * Delete a statistic with the given name. * * @param string $stat The name of the statistic to delete. * @return boolean True if successful, false if not. */ public static function clear($stat) { return (bool) Resque::redis()->del('stat:' . $stat); }
/** * Look for any workers which should be running on this server and if * they're not, remove them from Redis. * * This is a form of garbage collection to handle cases where the * server may have been killed and the workers did not die gracefully * and therefore leave state information in Redis. */ public function cleanup() { $workers = self::allWorkers(); $hosts = $this->redis->smembers(Host::redisKey()); $cleaned = array(); foreach ($workers as $worker) { list($host, $pid) = explode(':', (string) $worker, 2); if ($host != (string) $this->host and in_array($host, $hosts) or $host == (string) $this->host and posix_kill((int) $pid, 0)) { continue; } $this->log('Pruning dead worker: ' . $worker, Logger::DEBUG); $worker->unregister(); $cleaned[] = (string) $worker; } $workerIds = array_map(function ($w) { return (string) $w; }, $workers); $keys = (array) $this->redis->keys('worker:' . $this->host . ':*'); foreach ($keys as $key) { $key = $this->redis->removeNamespace($key); $id = substr($key, strlen('worker:')); if (!in_array($id, $workerIds)) { if ($this->redis->ttl($key) < 0) { $this->log('Expiring worker data: ' . $key, Logger::DEBUG); $this->redis->expire($key, \Resque::getConfig('default.expiry_time', \Resque::DEFAULT_EXPIRY_TIME)); } } } Event::fire(Event::WORKER_CLEANUP, array($this, $cleaned)); return $cleaned; }
/** * Stop tracking the status of a job. */ public function stop() { Resque::redis()->del((string) $this); }
/** * 异常日志入队列 * * @access public * @return void */ public function errorLog() { if (isset(Application::app()->getConfig()->application->queue) && isset(Application::app()->getConfig()->application->queue->redis) && isset(Application::app()->getConfig()->application->queue->log)) { $queue = Application::app()->getConfig()->application->queue->toArray(); $redis_config = $queue['redis']; $server = $redis_config['host'] . ':' . $redis_config['port']; $database = isset($redis_config['database']) ? $redis_config['database'] : null; Resque::setBackend($server, $database); $args = array('module' => $queue['log']['module'], 'controller' => $queue['log']['controller'], 'action' => $queue['log']['action'], 'args' => $this->toArray()); $queue_name = Application::app()->getConfig()->application->queue->log->name; Resque::enqueue($queue_name, 'Resque\\Job\\YafCLIRequest', $args, true); } }
/** * Create a new job and save it to the specified queue. * * @param string $queue The name of the queue to place the job in. * @param string $class The name of the class that contains the code to execute the job. * @param array $args Any optional arguments that should be passed when the job is executed. * @param boolean $trackStatus Set to true to be able to monitor the status of a job. * * @return string|boolean Job ID when the job was created, false if creation was cancelled due to beforeEnqueue */ public static function enqueue($queue, $class, $args = null, $trackStatus = false) { $id = Resque::generateJobId(); $hookParams = ['class' => $class, 'args' => $args, 'queue' => $queue, 'id' => $id]; try { Event::trigger('beforeEnqueue', $hookParams); } catch (Job\DontCreate $e) { return false; } Job::create($queue, $class, $args, $trackStatus, $id); Event::trigger('afterEnqueue', $hookParams); return $id; }
echo "Cannot find redis-server in path. Please make sure redis is installed.\n"; exit(1); } exec('cd ' . TEST_MISC . '; redis-server ' . REDIS_CONF, $output, $returnVar); usleep(500000); if ($returnVar != 0) { echo "Cannot start redis-server.\n"; exit(1); } // Get redis port from conf $config = file_get_contents(REDIS_CONF); if (!preg_match('#^\\s*port\\s+([0-9]+)#m', $config, $matches)) { echo "Could not determine redis port from redis.conf"; exit(1); } Resque::setBackend('localhost:' . $matches[1]); // Shutdown function killRedis($pid) { if (getmypid() !== $pid) { return; // don't kill from a forked worker } $config = file_get_contents(REDIS_CONF); if (!preg_match('#^\\s*pidfile\\s+([^\\s]+)#m', $config, $matches)) { return; } $pidFile = TEST_MISC . '/' . $matches[1]; if (file_exists($pidFile)) { $pid = trim(file_get_contents($pidFile)); posix_kill((int) $pid, 9);