├── images ├── queued │ └── empty ├── watermarked │ └── empty └── placeholder.png ├── .gitattributes ├── fonts └── built_titling_rg.ttf ├── .gitignore ├── images.php ├── composer.json ├── config.php ├── worker.php ├── readme.md ├── src ├── Message.php └── Queue.php ├── index.php └── tutorial.md /images/queued/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/watermarked/empty: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /images/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbgeorge/php-sqs-tutorial/HEAD/images/placeholder.png -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto 2 | * text eol=lf 3 | 4 | *.png binary 5 | *.jpg binary 6 | *.gif binary 7 | *.ttf binary -------------------------------------------------------------------------------- /fonts/built_titling_rg.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbgeorge/php-sqs-tutorial/HEAD/fonts/built_titling_rg.ttf -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Project specific files # 2 | ###################### 3 | /images/queued/* 4 | !/images/queued/empty 5 | /images/watermarked/* 6 | !/images/watermarked/empty 7 | vendor/ 8 | 9 | # IDE and editor specific files # 10 | ################################# 11 | .idea 12 | 13 | # OS generated files # 14 | ###################### 15 | .DS_Store 16 | .DS_Store? 17 | ._* 18 | .Spotlight-V100 19 | .Trashes 20 | Icon? 21 | ehthumbs.db 22 | Thumbs.db -------------------------------------------------------------------------------- /images.php: -------------------------------------------------------------------------------- 1 | 9 | * @license http://opensource.org/licenses/MIT MIT License 10 | * @link http://george.webb.uno/posts/aws-simple-queue-service-php-sdk 11 | */ 12 | 13 | $waiting_images = scandir(__DIR__ . '/images/queued'); 14 | $watermarked_images = scandir(__DIR__ . '/images/watermarked'); 15 | 16 | $output = array('waiting' => $waiting_images, 'watermarked' => $watermarked_images); 17 | 18 | echo json_encode($output); 19 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "gaw508/php-sqs-tutorial", 3 | "version" : "0.1.0", 4 | "description" : "Example for my AWS SQS tutorial", 5 | "license" : "MIT", 6 | "authors" : [ 7 | { 8 | "name" : "George Webb", 9 | "email" : "george@webb.uno", 10 | "homepage" : "http://george.webb.uno/", 11 | "role" : "Developer" 12 | } 13 | ], 14 | "require" : { 15 | "php" : ">=5.3.0", 16 | "aws/aws-sdk-php" : "3.*" 17 | }, 18 | "require-dev": { 19 | "squizlabs/php_codesniffer": "*" 20 | }, 21 | "autoload" : { 22 | "psr-4" : { 23 | "Gaw508\\PhpSqsTutorial\\" : "src" 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | 7 | * @license http://opensource.org/licenses/MIT MIT License 8 | * @link http://george.webb.uno/posts/aws-simple-queue-service-php-sdk 9 | */ 10 | 11 | /** 12 | * The name of the SQS queue 13 | */ 14 | define('QUEUE_NAME', 'watermarker'); 15 | 16 | /** 17 | * AWS Credentials array for accessing the API 18 | * 19 | * It is a serialised array, which is then unserialised when used. 20 | */ 21 | define('AWS_CREDENTIALS', serialize(array( 22 | 'region' => "[[AWS_REGION]]", 23 | 'version' => "[[AWS_VERSION]]", 24 | 'credentials' => array( 25 | 'key' => "[[AWS_ACCESS_KEY_ID]]", 26 | 'secret' => "[[AWS_SECRET_ACCESS_KEY]]" 27 | ) 28 | ))); 29 | -------------------------------------------------------------------------------- /worker.php: -------------------------------------------------------------------------------- 1 | 12 | * @license http://opensource.org/licenses/MIT MIT License 13 | * @link http://george.webb.uno/posts/aws-simple-queue-service-php-sdk 14 | */ 15 | 16 | require_once __DIR__ . '/config.php'; 17 | require_once __DIR__ . '/vendor/autoload.php'; 18 | 19 | use Gaw508\PhpSqsTutorial\Queue; 20 | 21 | // Instantiate queue with aws credentials from config. 22 | $queue = new Queue(QUEUE_NAME, unserialize(AWS_CREDENTIALS)); 23 | 24 | // Continuously poll queue for new messages and process them. 25 | while (true) { 26 | $message = $queue->receive(); 27 | if ($message) { 28 | try { 29 | $message->process(); 30 | $queue->delete($message); 31 | } catch (Exception $e) { 32 | $queue->release($message); 33 | echo $e->getMessage(); 34 | } 35 | } else { 36 | // Wait 20 seconds if no jobs in queue to minimise requests to AWS API 37 | sleep(20); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | PHP SQS Tutorial Demo 2 | ===================== 3 | 4 | For the article please visit: [http://george.webb.uno/posts/aws-simple-queue-service-php-sdk](http://george.webb.uno/posts/aws-simple-queue-service-php-sdk) 5 | 6 | There is a live demo version of this running at: [http://sqs-demo.george.webb.uno/](http://sqs-demo.george.webb.uno/) 7 | 8 | To install this demo for your self you will need a PHP web server running PHP 5.3 or later, composer, as well as PHP CLI with access to the imagick extension. 9 | 10 | ### Instructions: ### 11 | 12 | - Clone this git repo 13 | - Run composer install 14 | - Setup config.php with AWS credentials and SQS queue name 15 | - Run the worker with command: "php -d extension=imagick.so worker.php" 16 | - Visit index.php and upload some images. 17 | 18 | ### Quick explanation ### 19 | 20 | For more detail please see my tutorial article linked above, or the tutorial.md file in this repository. 21 | 22 | Essentially this is a demo showing a possible usage of SQS within a PHP application and an example implementation of the PHP AWS SDK. The demo involves a web page where a user can upload up to 10 images, which are put into a SQS queue to be watermarked. A separate component of the application is running in the background and watermarks each of these images from the queue. In reality the queue worker component would be running on a/many different servers and the image files would be stored remotely, rather than locally; however, for the sake of this demo, it is all done on the same machine. 23 | 24 | Here is a quick breakdown of the important parts of this application: 25 | 26 | - src/Queue.php is the class containing all the logic for communicating with SQS 27 | - src/Message.php is the class containing the logic involved with the messages themselves and the processing of them 28 | - index.php is the webpage which images are uploaded to and the results displayed 29 | - images.php is the API call used by the demo JS for displaying queued and watermarked images 30 | - worker.php is the queue worker which is run through the PHP CLI and processes the watermarking messages 31 | -------------------------------------------------------------------------------- /src/Message.php: -------------------------------------------------------------------------------- 1 | 9 | * @license http://opensource.org/licenses/MIT MIT License 10 | * @link http://george.webb.uno/posts/aws-simple-queue-service-php-sdk 11 | */ 12 | 13 | namespace Gaw508\PhpSqsTutorial; 14 | 15 | use Imagick; 16 | use ImagickDraw; 17 | 18 | class Message 19 | { 20 | /** 21 | * The path of the uploaded file to be processed 22 | * 23 | * @var string 24 | */ 25 | public $input_file_path; 26 | 27 | /** 28 | * The path to output the processed file 29 | * 30 | * @var string 31 | */ 32 | public $output_file_path; 33 | 34 | /** 35 | * The receipt handle from SQS, used to identify the message when interacting with the queue 36 | * 37 | * @var string 38 | */ 39 | public $receipt_handle; 40 | 41 | /** 42 | * Construct the object with message data and optional receipt_handle if relevant 43 | * 44 | * @param string|array $data JSON String or an assoc array containing the message data 45 | * @param string $receipt_handle The sqs receipt handle of the message 46 | */ 47 | public function __construct($data, $receipt_handle = '') 48 | { 49 | // If data is a json string, decode it into an assoc array 50 | if (is_string($data)) { 51 | $data = json_decode($data, true); 52 | } 53 | 54 | // Assign the data values and receipt handle to the object 55 | $this->input_file_path = $data['input_file_path']; 56 | $this->output_file_path = $data['output_file_path']; 57 | $this->receipt_handle = $receipt_handle; 58 | } 59 | 60 | /** 61 | * Returns the data of the message as a JSON string 62 | * 63 | * @return string JSON message data 64 | */ 65 | public function asJson() 66 | { 67 | return json_encode(array( 68 | 'input_file_path' => $this->input_file_path, 69 | 'output_file_path' => $this->output_file_path 70 | )); 71 | } 72 | 73 | /** 74 | * Processes an image given in the input file path, and outputs it in the output file path 75 | * 76 | * Takes the input image, creates a 300x300px thumbnail and overlays a text watermark. 77 | * Then deletes the input image. 78 | */ 79 | public function process() 80 | { 81 | // Crete Imagick object from input image 82 | $image = new Imagick($this->input_file_path); 83 | 84 | // Crops the image into a 300x300px thumbnail 85 | $image->cropthumbnailimage(300, 300); 86 | 87 | // Set the watermark text 88 | $text = 'WATERMARK!!!'; 89 | 90 | // Create a new drawing palette 91 | $draw = new ImagickDraw(); 92 | 93 | // Set font properties 94 | $draw->setFont(__DIR__ . '/../fonts/built_titling_rg.ttf'); 95 | $draw->setFontSize(26); 96 | $draw->setFillColor('black'); 97 | $draw->setGravity(Imagick::GRAVITY_CENTER); 98 | 99 | // Draw the watermark onto the image 100 | $image->annotateImage($draw, 10, 12, 0, $text); 101 | $draw->setFillColor('white'); 102 | $image->annotateImage($draw, 11, 11, 0, $text); 103 | 104 | // Set output image format 105 | $image->setImageFormat('jpg'); 106 | 107 | // Output the processed image to the output path (as .jpg) 108 | $output_path = explode('.', $this->output_file_path); 109 | array_pop($output_path); 110 | $output_path = implode($output_path) . '.jpg'; 111 | $image->writeImage($output_path); 112 | 113 | // Delete the input image 114 | unlink($this->input_file_path); 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/Queue.php: -------------------------------------------------------------------------------- 1 | 9 | * @license http://opensource.org/licenses/MIT MIT License 10 | * @link http://george.webb.uno/posts/aws-simple-queue-service-php-sdk 11 | */ 12 | 13 | namespace Gaw508\PhpSqsTutorial; 14 | 15 | use Exception; 16 | use Aws\Sqs\SqsClient; 17 | 18 | class Queue 19 | { 20 | /** 21 | * The name of the SQS queue 22 | * 23 | * @var string 24 | */ 25 | private $name; 26 | 27 | /** 28 | * The url of the SQS queue 29 | * 30 | * @var string 31 | */ 32 | private $url; 33 | 34 | /** 35 | * The array of credentials used to connect to the AWS API 36 | * 37 | * @var array 38 | */ 39 | private $aws_credentials; 40 | 41 | /** 42 | * A SqsClient object from the AWS SDK, used to connect to the AWS SQS API 43 | * 44 | * @var SqsClient 45 | */ 46 | private $sqs_client; 47 | 48 | /** 49 | * Constructs the wrapper using the name of the queue and the aws credentials 50 | * 51 | * @param $name 52 | * @param $aws_credentials 53 | */ 54 | public function __construct($name, $aws_credentials) 55 | { 56 | try { 57 | // Setup the connection to the queue 58 | $this->name = $name; 59 | $this->aws_credentials = $aws_credentials; 60 | $this->sqs_client = new SqsClient($this->aws_credentials); 61 | 62 | // Get the queue URL 63 | $this->url = $this->sqs_client->getQueueUrl(array('QueueName' => $this->name))->get('QueueUrl'); 64 | } catch (Exception $e) { 65 | echo 'Error getting the queue url ' . $e->getMessage(); 66 | } 67 | } 68 | 69 | /** 70 | * Sends a message to SQS using a JSON output from a given Message object 71 | * 72 | * @param Message $message A message object to be sent to the queue 73 | * @return bool returns true if message is sent successfully, otherwise false 74 | */ 75 | public function send(Message $message) 76 | { 77 | try { 78 | // Send the message 79 | $this->sqs_client->sendMessage(array( 80 | 'QueueUrl' => $this->url, 81 | 'MessageBody' => $message->asJson() 82 | )); 83 | 84 | return true; 85 | } catch (Exception $e) { 86 | echo 'Error sending message to queue ' . $e->getMessage(); 87 | return false; 88 | } 89 | } 90 | 91 | /** 92 | * Receives a message from the queue and puts it into a Message object 93 | * 94 | * @return bool|Message Message object built from the queue, or false if there is a problem receiving message 95 | */ 96 | public function receive() 97 | { 98 | try { 99 | // Receive a message from the queue 100 | $result = $this->sqs_client->receiveMessage(array( 101 | 'QueueUrl' => $this->url 102 | )); 103 | 104 | if ($result['Messages'] == null) { 105 | // No message to process 106 | return false; 107 | } 108 | 109 | // Get the message and return it 110 | $result_message = array_pop($result['Messages']); 111 | return new Message($result_message['Body'], $result_message['ReceiptHandle']); 112 | } catch (Exception $e) { 113 | echo 'Error receiving message from queue ' . $e->getMessage(); 114 | return false; 115 | } 116 | } 117 | 118 | /** 119 | * Deletes a message from the queue 120 | * 121 | * @param Message $message 122 | * @return bool returns true if successful, false otherwise 123 | */ 124 | public function delete(Message $message) 125 | { 126 | try { 127 | // Delete the message 128 | $this->sqs_client->deleteMessage(array( 129 | 'QueueUrl' => $this->url, 130 | 'ReceiptHandle' => $message->receipt_handle 131 | )); 132 | 133 | return true; 134 | } catch (Exception $e) { 135 | echo 'Error deleting message from queue ' . $e->getMessage(); 136 | return false; 137 | } 138 | } 139 | 140 | /** 141 | * Releases a message back to the queue, making it visible again 142 | * 143 | * @param Message $message 144 | * @return bool returns true if successful, false otherwise 145 | */ 146 | public function release(Message $message) 147 | { 148 | try { 149 | // Set the visibility timeout to 0 to make the message visible in the queue again straight away 150 | $this->sqs_client->changeMessageVisibility(array( 151 | 'QueueUrl' => $this->url, 152 | 'ReceiptHandle' => $message->receipt_handle, 153 | 'VisibilityTimeout' => 0 154 | )); 155 | 156 | return true; 157 | } catch (Exception $e) { 158 | echo 'Error releasing job back to queue ' . $e->getMessage(); 159 | return false; 160 | } 161 | } 162 | } 163 | -------------------------------------------------------------------------------- /index.php: -------------------------------------------------------------------------------- 1 | 8 | * @license http://opensource.org/licenses/MIT MIT License 9 | * @link http://george.webb.uno/posts/aws-simple-queue-service-php-sdk 10 | */ 11 | 12 | require_once __DIR__ . '/config.php'; 13 | require_once __DIR__ . '/vendor/autoload.php'; 14 | 15 | use Gaw508\PhpSqsTutorial\Message; 16 | use Gaw508\PhpSqsTutorial\Queue; 17 | 18 | // Array of messages to be displayed to the user. 19 | $warnings = array(); 20 | 21 | if ( !empty($_FILES)) { 22 | // check number of files to upload 23 | $number_of_images = count($_FILES['images']['name']); 24 | 25 | // Only upload a max of 10 files 26 | if ($number_of_images > 10) { 27 | $warnings[] = array( 28 | 'class' => 'alert-danger', 29 | 'text' => 'Too many images, please upload a maximum of 10 images.' 30 | ); 31 | } else { 32 | $successes = 0; 33 | 34 | // For each upload, check if an image and valid etc. 35 | for ($i = 0; $i < $number_of_images; $i++) { 36 | if ($_FILES['images']['error'][$i] > 0) { 37 | $warnings[] = array('class' => 'alert-danger', 'text' => 'Error uploading file.'); 38 | } elseif ( !filesize($_FILES['images']['tmp_name'][$i])) { 39 | $warnings[] = array('class' => 'alert-danger', 'text' => 'Error uploading file.'); 40 | } elseif ($_FILES['images']['type'][$i] != 'image/png' and $_FILES['images']['type'][$i] != 'image/jpeg') { 41 | $warnings[] = array('class' => 'alert-danger', 'text' => 'Invalid file type.'); 42 | } elseif ($_FILES['images']['size'][$i] > 2000000) { 43 | $warnings[] = array('class' => 'alert-danger', 'text' => 'File too big.'); 44 | } else { 45 | // Create a new filename for the uploaded image and move it there 46 | $extension = $_FILES['images']['type'][$i] == 'image/png' ? '.png' : '.jpg'; 47 | $new_name = uniqid() . $extension; 48 | if ( !move_uploaded_file($_FILES['images']['tmp_name'][$i], __DIR__ . '/images/queued/' . $new_name)) { 49 | $warnings[] = array('class' => 'alert-danger', 'text' => 'Error uploading file.'); 50 | } else { 51 | // Create a new message with processing instructions and push to SQS queue 52 | $message = new Message(array( 53 | 'input_file_path' => __DIR__ . '/images/queued/' . $new_name, 54 | 'output_file_path' => __DIR__ . '/images/watermarked/' . $new_name 55 | )); 56 | $queue = new Queue(QUEUE_NAME, unserialize(AWS_CREDENTIALS)); 57 | if ($queue->send($message)) { 58 | $successes++; 59 | } else { 60 | $warnings[] = array('class' => 'alert-danger', 'text' => 'Error adding file to queue.'); 61 | } 62 | } 63 | } 64 | } 65 | 66 | if ($successes > 0) { 67 | $warnings[] = array('class' => 'alert-success', 'text' => "$successes images uploaded successfully."); 68 | $warnings[] = array('class' => 'alert-info', 'text' => "Uploaded images added to queue..."); 69 | } 70 | } 71 | } 72 | 73 | ?> 74 | 75 | 76 | 77 | PHP SQS Demo 78 | 79 | 80 | 81 |
82 | 88 | 89 |
90 | This is a demo built to show the example given in my tutorial article, which can be found at 91 | 92 | http://george.webb.uno/posts/aws-simple-queue-service-php-sdk 93 | 94 |
95 | 96 | 97 | 98 |
99 | 100 | 101 | 102 |
103 |
104 |
105 |
106 |

Upload multiple images to have them watermarked

107 |
108 | 109 |
110 |
111 |
112 | 113 | 114 |

Choose multiple jpg or png files.

115 |
116 | 117 |
118 |
119 |
120 |
121 | 122 |
123 |
124 |
125 |

Watermarked images

126 |
127 | 128 |
129 |
130 | Heads up! Images are deleted after one hour 131 |
132 | 133 |
134 | Loading ... 135 |
136 |
137 |
138 |
139 |
140 |
141 | 142 | 143 | 144 | 188 | 189 | 190 | -------------------------------------------------------------------------------- /tutorial.md: -------------------------------------------------------------------------------- 1 | Using AWS Simple Queue Service with the PHP SDK 2 | =============================================== 3 | 4 | Amazon Web Services Simple Queue Service is awesome. 5 | 6 | For those of you who don't know, SQS is a highly scalable and reliable distributed queueing system, which can be used to separate components of an application. This article goes into how to utilise SQS using the PHP SDK and concludes with an example of how it might be used. 7 | 8 | **Contents:** 9 | 10 | - Setting up the PHP SDK 11 | - Creating queues 12 | - Sending messages 13 | - Receiving messages 14 | - Dealing with failures 15 | - An example 16 | 17 | ## Setting up the PHP SDK ## 18 | 19 |
20 | 21 | Amazon provides a fantastic PHP SDK for its web services, more info can be found at [http://aws.amazon.com/sdk-for-php/](http://aws.amazon.com/sdk-for-php/) 22 | 23 | **Installing the SDK** 24 | 25 | The best way to install the SDK is by using Composer, all you will need to do is require aws/aws-sdk-php and include the autoloader. If you are unfamiliar with composer, I thoroughly recommend that you take a look at [https://getcomposer.org/](https://getcomposer.org/), otherwise, you can install the SDK by downloading a zip from github [https://github.com/aws/aws-sdk-php/releases](https://github.com/aws/aws-sdk-php/releases) 26 | 27 | **Authentication** 28 | 29 | When communicating with the AWS API, the SDK requires the following: 30 | 31 | - The AWS region (e.g. 'eu-west-1') 32 | - The version of the AWS API to use ('latest' will usually be fine) 33 | - Your AWS Access Key ID (Under 'Your Security Credentials') 34 | - Your AWS Secret Access key (Under 'Your Security Credentials') 35 | 36 | These are passed to the SDK in the form of an array: 37 | 38 | $aws_credentials = array( 39 | 'region' => AWS_REGION, 40 | 'version' => AWS_VERSION, 41 | 'credentials' => array( 42 | 'key' => AWS_ACCESS_KEY_ID, 43 | 'secret' => AWS_SECRET_ACCESS_KEY, 44 | ) 45 | ); 46 | 47 | **Handling errors** 48 | 49 | In the case of errors the SDK will throw exceptions, therefore we need to use try catch blocks in our code. 50 | 51 | ## Creating queues ## 52 | 53 |
54 | 55 | Queues have a small number of settings we can use to configure it for our needs, however the only mandatory one is a name, which is used to identify the queue when sending and receiving messages. There are other options including how long messages should retained and the size of messages. For the sake of simplicity I will only use the name here. Queues can be created easily either in the AWS console, or by using the API. 56 | 57 | To interact with our queues, we will be using the SqsClient class provided by the SDK, all this requires is our credentials array mentioned above, and its that simple. To create a queue we will use the SqsClient::createQueue() method, nice and straightforward, ey? The below code creates a queue called "our_queue". In reality this code will be used infrequently, compared to the sending and receiving of messages, but it is included in case your application makes use of dynamically creating queues. 58 | 59 | try { 60 | $sqs_credentials = array( 61 | 'region' => '[[YOUR_AWS_REGION]]', 62 | 'version' => 'latest', 63 | 'credentials' => array( 64 | 'key' => '[[YOUR_AWS_ACCESS_KEY_ID]]', 65 | 'secret' => '[[YOUR_AWS_SECRET_ACCESS_KEY]]', 66 | ) 67 | ); 68 | 69 | // Instantiate the client 70 | $sqs_client = new SqsClient($sqs_credentials); 71 | 72 | // Create the queue 73 | $queue_options = array( 74 | 'QueueName' => 'our_queue' 75 | ); 76 | $sqs_client->createQueue($queue_options); 77 | } catch (Exception $e) { 78 | die('Error creating new queue ' . $e->getMessage()); 79 | } 80 | 81 | ## Sending messages ## 82 | 83 |
84 | 85 | The next part is to send messages to the queue, this will be performed by the component(s) of the application which is effectively "delegating" jobs to another component(s) of the application. The messages will need to contain the information required by the other component of the application to process the job, a good way to do this is to use JSON, but XML or other methods could be used. 86 | 87 | Once again it is a very simple operation thanks to Amazon's excellent SDK. The below code adds a JSON message to "our_queue" using the SqsClient::sendMessage() method. 88 | 89 | try { 90 | $sqs_credentials = array( 91 | 'region' => '[[YOUR_AWS_REGION]]', 92 | 'version' => 'latest', 93 | 'credentials' => array( 94 | 'key' => '[[YOUR_AWS_ACCESS_KEY_ID]]', 95 | 'secret' => '[[YOUR_AWS_SECRET_ACCESS_KEY]]', 96 | ) 97 | ); 98 | 99 | // Instantiate the client 100 | $sqs_client = new SqsClient($sqs_credentials); 101 | 102 | // Get the queue URL from the queue name. 103 | $result = $sqs_client->getQueueUrl(array('QueueName' => "our_queue")); 104 | $queue_url = $result->get('QueueUrl'); 105 | 106 | // The message we will be sending 107 | $our_message = array('foo' => 'blah', 'bar' => 'blah blah'); 108 | 109 | // Send the message 110 | $sqs_client->sendMessage(array( 111 | 'QueueUrl' => $queue_url, 112 | 'MessageBody' => json_encode($our_message) 113 | )); 114 | } catch (Exception $e) { 115 | die('Error sending message to queue ' . $e->getMessage()); 116 | } 117 | 118 | ## Receiving messages ## 119 | 120 |
121 | 122 | The component of the application whose purpose is to process the messages needs to be able to get them from the queue, this is done by using the SqsClient::receiveMessage() method. 123 | 124 | try { 125 | $sqs_credentials = array( 126 | 'region' => '[[YOUR_AWS_REGION]]', 127 | 'version' => 'latest', 128 | 'credentials' => array( 129 | 'key' => '[[YOUR_AWS_ACCESS_KEY_ID]]', 130 | 'secret' => '[[YOUR_AWS_SECRET_ACCESS_KEY]]', 131 | ) 132 | ); 133 | 134 | // Instantiate the client 135 | $sqs_client = new SqsClient($sqs_credentials); 136 | 137 | // Get the queue URL from the queue name. 138 | $result = $sqs_client->getQueueUrl(array('QueueName' => "our_queue")); 139 | $queue_url = $result->get('QueueUrl'); 140 | 141 | // Receive a message from the queue 142 | $result = $sqs_client->receiveMessage(array( 143 | 'QueueUrl' => $queue_url 144 | )); 145 | 146 | if ($result['Messages'] == null) { 147 | // No message to process 148 | exit; 149 | } 150 | 151 | // Get the message information 152 | $result_message = array_pop($result['Messages']); 153 | $queue_handle = $result_message['ReceiptHandle']; 154 | $message_json = $result_message['Body']; 155 | 156 | // Do some processing... 157 | 158 | } catch (Exception $e) { 159 | die('Error receiving message to queue ' . $e->getMessage()); 160 | } 161 | 162 | From the message received, we have got the message JSON, which we use to process the message relating to our application, and the receipt handle, which we use to close off the message when we successfully finish processing it. Closing off the message (deleting it) is shown below using the SqsClient::deleteMessage() method. 163 | 164 | try { 165 | $sqs_credentials = array( 166 | 'region' => '[[YOUR_AWS_REGION]]', 167 | 'version' => 'latest', 168 | 'credentials' => array( 169 | 'key' => '[[YOUR_AWS_ACCESS_KEY_ID]]', 170 | 'secret' => '[[YOUR_AWS_SECRET_ACCESS_KEY]]', 171 | ) 172 | ); 173 | 174 | // Instantiate the client 175 | $sqs_client = new SqsClient($sqs_credentials); 176 | 177 | // Get the queue URL from the queue name. 178 | $result = $sqs_client->getQueueUrl(array('QueueName' => "our_queue")); 179 | $queue_url = $result->get('QueueUrl'); 180 | 181 | $sqs_client->deleteMessage(array( 182 | 'QueueUrl' => $queue_url, 183 | 'ReceiptHandle' => $queue_handle 184 | )); 185 | } catch (Exception $e) { 186 | die('Error deleting job from queue ' . $e->getMessage()); 187 | } 188 | 189 | ## Dealing with failures ## 190 | 191 |
192 | 193 | There are several issues that can arise in this process, which can all be easily mitigated using careful thinking: 194 | 195 | 1) The component receiving and processing the messages fails whilst processing a message. If the message is deleted from the queue after this event it will never be processed, so it is important these errors are handled correctly, and jobs are given back to the queue. This can be done by setting the message visibility timeout to 0, making it instantly visible in the queue to be tried again. 196 | 197 | try { 198 | $sqs_credentials = array( 199 | 'region' => '[[YOUR_AWS_REGION]]', 200 | 'version' => 'latest', 201 | 'credentials' => array( 202 | 'key' => '[[YOUR_AWS_ACCESS_KEY_ID]]', 203 | 'secret' => '[[YOUR_AWS_SECRET_ACCESS_KEY]]', 204 | ) 205 | ); 206 | 207 | // Instantiate the client 208 | $sqs_client = new SqsClient($sqs_credentials); 209 | 210 | // Get the queue URL from the queue name. 211 | $result = $sqs_client->getQueueUrl(array('QueueName' => "our_queue")); 212 | $queue_url = $result->get('QueueUrl'); 213 | 214 | $sqs_client->changeMessageVisibility(array( 215 | 'QueueUrl' => $queue_url, 216 | 'ReceiptHandle' => $queue_handle, 217 | 'VisibilityTimeout' => 0 218 | )); 219 | } catch (Exception $e) { 220 | die('Error releasing job back to queue ' . $e->getMessage()); 221 | } 222 | 223 | 2) The receiving and processing component gets stuck processing a message and it is never deleted or released back to the queue. For this scenario, the queue has a visibilityTimeout setting, which is how long after a message is received before it is automatically added back to the queue if it is not released or deleted. This can be set depending on how long the message processing is expected to take in the application. 224 | 225 | 3) A message is repeatedly received and failed, maybe because it is erroneous or corrupted. In this situation, a maximum number of receives can be set on a queue, which prevents a message from being attempted any more times than this value. You can let this drop the message off the queue, or you can configure a separate queue called a dead letter queue, into which these repeatedly failed messages will be put. You can then process this queue accordingly to handle this scenario. 226 | 227 | ## An example ## 228 | 229 |
230 | 231 | Say we have an application where users can upload multiple images to a web page, and those images would subsequently be watermarked before being displayed back to the user. If the user is uploading multiple images, it could take time and processing power, so we may want to watermark the images in the background, potentially using other servers dedicated to this task alone. We can achieve this by using SQS as a centralised queue containing a message for each of the images which need to be watermarked, giving information such as the path of the image and the path to save the output image. 232 | 233 | So, we have our web page where users can upload images, which are then saved, and messages sent to the queue for watermarking. We then also have a worker script, which polls the queue for images to watermark and then watermarks them and saves them in the relevant location. The final images are then displayed to the user. 234 | 235 | I have created a demo for this example, which can be viewed at [http://sqs-demo.george.webb.uno/](http://sqs-demo.george.webb.uno/) and the code can be found at [https://github.com/gaw508/php-sqs-tutorial](https://github.com/gaw508/php-sqs-tutorial) 236 | 237 | Information on how to install your own version of the demo and briefly how it works can be seen in the readme.md in the git repository. 238 | 239 | If you have any questions about this tutorial or the demo, please get in touch either by email or in the comments below. --------------------------------------------------------------------------------