├── .gitmodules ├── LICENSE ├── README ├── config.php ├── creator.php ├── randomsayings.txt ├── snippits.php ├── solo ├── status.php └── worker.php /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "predis"] 2 | path = predis 3 | url = https://github.com/nrk/predis 4 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Use it however you'd like. 2 | 3 | Unless it is to take over the world... 4 | 5 | Then you can't do that. Thanks. 6 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | Examples for PHP Workers with Redis & Solo 2 | 3 | Article URL: http://www.justincarmony.com/blog/2012/01/10/php-workers-with-redis-solo/ 4 | -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | 7 | * Example: php creator.php 50 8 | * 9 | */ 10 | 11 | echo "Creator Script Starting...\n"; 12 | 13 | require 'config.php'; 14 | 15 | $predis = new Predis\Client(array( 16 | 'scheme' => 'tcp', 17 | 'host' => REDIS_HOST, 18 | 'port' => REDIS_PORT, 19 | )); 20 | 21 | echo "Connected to Redis Server \n"; 22 | 23 | $jobs_to_create = $argv[1]; // Get how many jobs to create 24 | 25 | // If not passed, set default of 10 26 | if(!$jobs_to_create) 27 | { 28 | $jobs_to_create = 10; // Default 29 | } 30 | 31 | echo "Creating Jobs: $jobs_to_create \n"; 32 | 33 | $messages_str = file_get_contents('randomsayings.txt'); 34 | $messages = explode("\n", $messages_str); 35 | 36 | $messages_count = count($messages); 37 | 38 | $count = 0; 39 | 40 | while($count < $jobs_to_create) 41 | { 42 | $count++; 43 | 44 | $job = new stdClass(); 45 | $job->wait = rand(0, 500000); 46 | $job->loops = rand(2,10); 47 | $job->message = $messages[rand(0, $messages_count - 1)]; 48 | 49 | $job_json = json_encode($job); 50 | 51 | 52 | // Randomly select which queue to use 53 | $rand = rand(0, 10); 54 | $queue_name = ''; 55 | 56 | if($rand >= 10) 57 | { 58 | $queue_name = 'queue.priority.high'; 59 | } 60 | else if($rand >= 6) 61 | { 62 | $queue_name = 'queue.priority.normal'; 63 | } 64 | else 65 | { 66 | $queue_name = 'queue.priority.low'; 67 | } 68 | 69 | $predis->rpush($queue_name, $job_json); 70 | echo "Job [$count] Generated. \n"; 71 | } 72 | 73 | echo "Done!\nGoodbye...\n"; 74 | -------------------------------------------------------------------------------- /randomsayings.txt: -------------------------------------------------------------------------------- 1 | Patience is a virtue. 2 | Three's a crowd. 3 | 43% of all statistics are wrong. 4 | There are 10 types of people; those who understand binary and those who don't. 5 | After all is said and done, more is said than done. 6 | The biggest man you ever did see was once a baby. 7 | It's only a game! -------------------------------------------------------------------------------- /snippits.php: -------------------------------------------------------------------------------- 1 | 'tcp', 18 | 'host' => REDIS_HOST, 19 | 'port' => REDIS_PORT, 20 | )); 21 | 22 | 23 | /* 24 | * Adding items to the queue 25 | */ 26 | 27 | $job = new stdClass(); 28 | $job->id = 1; 29 | $job->report = 'general'; 30 | 31 | // Add the job to the high priority queue 32 | $predis->rpush('queue.priority.high', json_encode($job)); 33 | 34 | // Or, you could add it to the normal or low priority queue. 35 | $predis->rpush('queue.priority.normal', json_encode($job)); 36 | $predis->rpush('queue.priority.low', json_encode($job)); 37 | 38 | /* 39 | * Simple Continuous While Loop 40 | */ 41 | 42 | // Always True 43 | while(1) 44 | { 45 | /* ... perform tasks here ... */ 46 | } 47 | 48 | /* 49 | * Checking the Queue 50 | */ 51 | $job = $predis->blpop('queue.priority.high' 52 | , 'queue.priority.normal' 53 | , 'queue.priority.low' 54 | , 10); 55 | 56 | /* 57 | * Checking to see if a Job was returned 58 | */ 59 | 60 | if($job) 61 | { 62 | // Index 0 of the array holds which queue was returned 63 | $queue_name = $job[0]; 64 | // Index 1 of the array holds the string value of the job. 65 | // Since we are passing it JSON, we'll decode it: 66 | $details = json_decode($job[1]); 67 | 68 | /* ... do job work ... */ 69 | } 70 | 71 | /* 72 | * A Smarter While Statement 73 | */ 74 | 75 | // Set the time limit for php to 0 seconds 76 | set_time_limit(0); 77 | 78 | /* 79 | * We'll set our base time, which is one hour (in seconds). 80 | * Once we have our base time, we'll add anywhere between 0 81 | * to 10 minutes randomly, so all workers won't quick at the 82 | * same time. 83 | */ 84 | $time_limit = 60 * 60 * 1; // Minimum of 1 hour 85 | $time_limit += rand(0, 60 * 10); // Adding additional time 86 | 87 | // Set the start time 88 | $start_time = time(); 89 | 90 | // Continue looping as long as we don't go past the time limit 91 | while(time() < $start_time + $time_limit) 92 | { 93 | /* ... perorm BLPOP command ... */ 94 | /* ... process jobs when received ... */ 95 | } 96 | 97 | /* ... will quit once the time limit has been reached ... */ 98 | 99 | /* 100 | * Assigning Worker IDs & Monitoring 101 | * 102 | * Usage: php worker.php 1 103 | */ 104 | 105 | // Gets the worker ID from the command line argument 106 | $worker_id = $argv[1]; 107 | 108 | // Setting the Worker's Status 109 | $predis->hset('worker.status', $worker_id, 'Started'); 110 | 111 | // Set the last time this worker checked in, use this to 112 | // help determine when scripts die 113 | $predis->hset('worker.status.last_time', $worker_id, time()); 114 | 115 | 116 | /* 117 | * Using Versions to Check for Reloads 118 | */ 119 | 120 | $version = $predis->get('worker.version'); // i.e. number: 6 121 | 122 | while(time() < $start_time + $time_limit) 123 | { 124 | /* ... check for jobs and process them ... */ 125 | 126 | /* ... then, at the very end of the while ... */ 127 | if($predis->get('worker.version') != $version) 128 | { 129 | echo "New Version Detected... \n"; 130 | echo "Reloading... \n"; 131 | exit(); 132 | } 133 | } -------------------------------------------------------------------------------- /solo: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl -s 2 | # 3 | # solo v1.5 4 | # Prevents multiple cron instances from running simultaneously. 5 | # 6 | # Copyright 2007-2010 Timothy Kay 7 | # http://timkay.com/solo/ 8 | # 9 | # This program is free software: you can redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as published by 11 | # the Free Software Foundation, either version 3 of the License, or 12 | # (at your option) any later version. 13 | # 14 | # This program is distributed in the hope that it will be useful, 15 | # but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License 20 | # along with this program. If not, see . 21 | # 22 | 23 | use Socket; 24 | 25 | alarm $timeout if $timeout; 26 | 27 | $port =~ /^\d+$/ or $noport or die "Usage: $0 -port=PORT COMMAND\n"; 28 | 29 | if ($port) 30 | { 31 | $addr = pack(CnC, 127, $<, 1); 32 | print "solo: bind ", join(".", unpack(C4, $addr)), ":$port\n" if $verbose; 33 | 34 | $^F = 10; # unset close-on-exec 35 | 36 | socket(SOLO, PF_INET, SOCK_STREAM, getprotobyname('tcp')) or die "socket: $!"; 37 | bind(SOLO, sockaddr_in($port, $addr)) or $silent? exit: die "solo($port): $!\n"; 38 | } 39 | 40 | sleep $sleep if $sleep; 41 | 42 | exec @ARGV; 43 | -------------------------------------------------------------------------------- /status.php: -------------------------------------------------------------------------------- 1 | 7 | * Example: php status.php 5 8 | * 9 | */ 10 | 11 | echo "Creator Script Starting...\n"; 12 | 13 | require 'config.php'; 14 | 15 | $predis = new Predis\Client(array( 16 | 'scheme' => 'tcp', 17 | 'host' => REDIS_HOST, 18 | 'port' => REDIS_PORT, 19 | )); 20 | 21 | echo "Connected to Redis Server \n"; 22 | 23 | $delay = $argv[1]; // Get how many jobs to create 24 | 25 | if(!$delay) 26 | { 27 | $delay = 3; // Default delay of 3 seconds. 28 | } 29 | 30 | // Loop indefinitely 31 | while(1) 32 | { 33 | // Get the status 34 | $queue_lengths = array(); 35 | $queue_lengths['high'] = $predis->llen('queue.priority.high'); 36 | $queue_lengths['normal'] = $predis->llen('queue.priority.normal'); 37 | $queue_lengths['low'] = $predis->llen('queue.priority.low'); 38 | 39 | $queue_total = 0; 40 | // Change null values to 0's 41 | foreach($queue_lengths as $name => $size) 42 | { 43 | if($size == null) 44 | { 45 | $queue_lengths[$name] = 0; 46 | } 47 | 48 | $queue_total += $queue_lengths[$name]; 49 | } 50 | 51 | // Trim out old workers that haven't "worked" in over an hour 52 | $workers_time = $predis->hgetall('worker.status.last_time'); 53 | $time_limit = time() - 60 * 60 * 1; 54 | 55 | foreach($workers_time as $worker_id => $worker_ts) 56 | { 57 | if($worker_ts < $time_limit) 58 | { 59 | $predis->hdel('worker.status', $worker_id); 60 | $predis->hdel('worker.status.last_time', $worker_id); 61 | } 62 | } 63 | 64 | $workers = $predis->hgetall('worker.status'); 65 | ksort($workers); 66 | 67 | // Display Queue status 68 | echo "\n----------------------\n"; 69 | echo "Queue Statuses:\n\n"; 70 | echo " High: ".$queue_lengths['high']."\n"; 71 | echo " Normal: ".$queue_lengths['normal']."\n"; 72 | echo " Low: ".$queue_lengths['low']."\n\n"; 73 | echo " Total: ".$queue_total."\n\n"; 74 | 75 | echo "Worker Statuses:\n\n"; 76 | 77 | foreach($workers as $worker_id => $status) 78 | { 79 | if($worker_id > 0) 80 | { 81 | echo " Worker [$worker_id]: $status \n"; 82 | } 83 | } 84 | 85 | echo "----------------------\n"; 86 | 87 | // Sleep the delay 88 | sleep($delay); 89 | } 90 | 91 | 92 | -------------------------------------------------------------------------------- /worker.php: -------------------------------------------------------------------------------- 1 | 'tcp', 22 | 'host' => REDIS_HOST, 23 | 'port' => REDIS_PORT, 24 | )); 25 | 26 | // Setting the Worker's Status 27 | $predis->hset('worker.status', $worker_id, 'Started'); 28 | $predis->hset('worker.status.last_time', $worker_id, time()); 29 | 30 | // Set the time limit for php to 0 seconds 31 | set_time_limit(0); 32 | 33 | /* 34 | * We'll set our base time, which is one hour (in seconds). 35 | * Once we have our base time, we'll add anywhere between 0 36 | * to 10 minutes randomly, so all workers won't quick at the 37 | * same time. 38 | */ 39 | $time_limit = 60 * 60 * 1; // Minimum of 1 hour 40 | $time_limit += rand(0, 60 * 10); // Adding additional time 41 | 42 | // Set the start time 43 | $start_time = time(); 44 | 45 | echo " Waiting for a Job ."; 46 | 47 | // Continue looping as long as we don't go past the time limit 48 | while(time() < $start_time + $time_limit) 49 | { 50 | // Setting the Worker's Status 51 | $predis->hset('worker.status', $worker_id, 'Waiting'); 52 | $predis->hset('worker.status.last_time', $worker_id, time()); 53 | 54 | // Check to see if there are any items in the queues in 55 | // order of priority. If all are empty, wait up to 10 56 | // seconds for something to be added to the queue. 57 | $job = $predis->blpop('queue.priority.high' 58 | , 'queue.priority.normal' 59 | , 'queue.priority.low' 60 | , 10); 61 | 62 | // If a job was pulled out 63 | if($job) 64 | { 65 | /* 66 | * Start Working 67 | * 68 | * This is where you will use the data from the 69 | */ 70 | echo "\nJob Started "; 71 | // Setting the Worker's Status 72 | $predis->hset('worker.status', $worker_id, 'Working'); 73 | $predis->hset('worker.status.last_time', $worker_id, time()); 74 | 75 | $queue_name = $job[0]; // 0 is the name of the queue 76 | $details = json_decode($job[1]); // parse the json data from the job 77 | 78 | /* Progress Dots Logic 79 | * 80 | * To make our application look cooler, we will print out a "." 81 | * for every time we wait. The final result is will look as such: 82 | * 83 | * Job Started .... Done! 84 | */ 85 | $dot_counter =0; 86 | $dot_limit = $details->loops; 87 | while($dot_counter < $dot_limit) 88 | { 89 | usleep($details->wait); 90 | echo "."; 91 | $dot_counter++; 92 | } 93 | echo " Done!\n Message from $queue_name: $details->message \n"; 94 | echo " Waiting for a Job ."; 95 | } 96 | else 97 | { 98 | /* Waiting Dots Logic 99 | * 100 | * 101 | */ 102 | echo "."; 103 | } 104 | } 105 | 106 | // Setting the Worker's Status 107 | $predis->hset('worker.status', $worker_id, 'Closed'); 108 | $predis->hset('worker.status.last_time', $worker_id, time()); 109 | 110 | echo "\n\nWorker [$worker_id] Finished! \nGoodbye...\n"; --------------------------------------------------------------------------------