Key Takeaways
- Zend Server’s Job Queue module allows for asynchronous execution of non-interactive and long-running tasks, allowing tasks to run in parallel, be deferred or executed periodically, and be managed via a GUI.
- The Job Queue API is accessible through the ZendJobQueue class, which allows for the creation of jobs, passing of parameters, and setting of additional job options such as priority, persistence, and scheduling.
- The Job Queue can be utilized to improve user experience and application efficiency, as demonstrated in the extended example of generating and emailing reports, where tasks can be scheduled and executed in parallel, reducing wait time for users.
- While there are alternatives to Zend Server’s Job Queue for handling queues and parallel processing in PHP, such as cron, pcntl_fork, Gearman, node.js, and RabbitMQ, the Job Queue provides a straightforward solution that is easy to start using.
- Running tasks now without waiting for them to finish (asynchronous execution)
- Running tasks once but not right now (deferred jobs)
- Running tasks periodically (recurring jobs like cron, but with full control over them from through the PHP API – start, stop, suspend, resume)
- Ability to query job status, handle failures, and re-queue via the API as well as keep track of past, current, and pending jobs from the GUI.
- Preparing data for the next request (pre-calculating)
- Pre-caching data
- Generating periodical reports
- Sending e-mails
- Cleaning temporary data or files
- Communicating with external systems
- Background data synchronization with mobile devices
Job Queue Usage
Job Queue’s API is made available through theZendJobQueue
class. To perform most tasks you will connect to a Job Queue server by instantiating a ZendJobQueue
object and create a job using the createHttpJob()
method.
<?php
$queue = new ZendJobQueue();
$queue->createHttpJob("http://example.com/jobs/somejob.php");
Passing a path to createHttpJob()
instead of a full URL will create a job with the value of $_SERVER["HTTP_HOST"]
as the host name. Watch out for instances when $_SERVER["HTTP_HOST"]
is not available, such as when the job is scheduled from a cron script.
<?php
// both calls are equivalent
$queue->createHttpJob("/jobs/somejob.php");
$queue->createHttpJob("http://" . $_SERVER["HTTP_HOST"] . "/jobs/somejob.php");
Job parameters can be passed either as a part of the query string or in an array as the second argument of createHttpJob()
. If parameters are passed as the second argument, the array must be JSON compatible.
To access the parameters, the getCurrentJobParams()
static method can be used inside the job code.
<?php
$params = ZendJobQueue::getCurrentJobParams();
Additional job options are available via a third argument to createHttpJob()
. It is an associative array with the following keys:
name
– an optional job namepriority
– the job priority as defined by the corresponding constantsPRIORITY_LOW
,PRIORITY_NORMAL
,PRIORITY_HIGH
, andPRIORITY_URGENT
persistent
– a Boolean value whether to keep the job in history foreverpredecessor
– an integer predecessor job IDhttp_headers
– additional HTTP headersschedule
– cron-like scheduling commandschedule_time
– time when the job should be executed (but it may actually run after that time depending on Job Queue’s load)
<?php
$params = array("p1" => 10, "p2" => "somevalue");
// process in one hour
$options = array("schedule_time" => date("Y-m-d H:i:s", strtotime("+1 hour")));
$queue->createHttpJob("http://example.com/jobs/somejob.php", $params, $options);
// process every other day at 1:05 am
$options = array("schedule" => "5 1 */2 * *");
$queue->createHttpJob("http://example.com/jobs/somejob.php", $params, $options);
Failures (and successes) can be handled in the following manner:
<?php
try {
doSomething();
ZendJobQueue::setCurrentJobStatus(ZendJobQueue::OK);
}
catch (Exception $e) {
ZendJobQueue::setCurrentJobStatus(ZendJobQueue::STATUS_LOGICALLY_FAILED, $e->getMessage());
}
An Extended Example
Let’s say your web application has to generate and e-mail a set of reports upon a user’s request. Typically, given the fact that PHP does not support multiprocessing and synchronous communication model is used, the user will have to wait until all of the requested reports are generated one by one and e-mailed. Using Job Queue in this case will not only allow the user to perform other actions with the application (since the work will be done asynchronously) but also the application can process multiple reports at the same time (since jobs can be executed in parallel) – so most of the reports (if not all) will finish at about the same time.<?php
function scheduleReport($reportList, $recipient) {
// list of scheduled jobs
$jobList = array();
$queue = new ZendJobQueue();
// check that Job Queue is running
if ($queue->isJobQueueDaemonRunning() && count($reportList) > 0) {
foreach ($reportList as $report) {
$params = array("type" => $report["type"],
"start" => $report["start"],
"length" => $report["length"],
"recipient" => $recipient);
$options = array("priority" => $report["priority"]);
// execute the job in two minutes unless the priority is urgent
if ($report["priority"] != ZendJobQueue::PRIORITY_URGENT) {
$options["schedule_time"] = date("Y-m-d H:i:s", strtotime("+2 minutes"));
}
$jobID = $queue->createHttpJob("http://example.com/jobs/report.php", $params, $options);
// add job ID to the list of successfully scheduled jobs
if ($jobID !== false) {
$jobList[] = $jobID;
}
}
return $jobList;
}
The scheduleReport()
function returns the list of job identifiers associated with each scheduled report. Within this function, the isJobQueueDaemonRunning()
method of ZendJobQueue
class verifies that the appropriate service is running and jobs can be scheduled.
Depending on the report’s priority, the job can be scheduled to run immediately or two minutes later (in an effort to reduce the load on the server if many reports are requested at the same time). Once a job is scheduled, its ID is saved to the list of all successfully created jobs. It’s important to know a job’s ID in order to be able to monitor the job or even cancel it.
There’s what the call to scheduleReport()
function looks like:
<?php
// setup request for a daily sales report and monthly financial report
$reportList = array(
array("type" => "sales",
"start" => "2011-12-09 00:00:00",
"length" => 1,
"priority" => ZendJobQueue::PRIORITY_URGENT),
array("type" => "finance",
"start" => "2011-11-01 00:00:00",
"length" => 30,
"priority" => ZendJobQueue::PRIORITY_NORMAL));
// schedule reports
$jobList = scheduleReport($reportList, "user@example.com");
// verify that reports were scheduled
if (empty($jobList)) {
// show error message
}
As mentioned earlier, it is also possible cancel a scheduled job. Once the job is in progress though it will be finished. Thus, if the priority of the request is not urgent, the user has two minutes to cancel the delivery of the scheduled report.
<?php
function cancelReport($jobID) {
$queue = new ZendJobQueue();
return $queue->removeJob($jobID);
}
if ($jobID !== false && cancelReport($jobID)) {
// the job was successfully removed from the queue
}
The cancelReport()
function simply removes the job from the queue of scheduled reports which have not yet started to run.
The job script then looks like this:
<?php
function runReport() {
$params = ZendJobQueue::GetParamList();
try {
$report = prepareReport($params["type"], $params["start"], $params["length"]);
sendReport($params["recipient"], $report);
ZendJobQueue::setCurrentJobStatus(ZendJobQueue::OK);
}
catch (Exception $e) {
ZendJobQueue::setCurrentJobStatus(ZendJobQueue::STATUS_LOGICALLY_FAILED, $e->getMessage());
}
}
The runReport()
function finally prepares and sends the report based on provided parameters. After completion, the job status is set as successful (or logically failed if there was an error).
Alternatives
Of course there are alternatives to Job Queue. Solutions like cron, pcntl_fork, or even something Java based via PHP/Java Bridge may or may not be worth looking into depending on your need. More interesting tools also exist, such as Gearman, node.js, and RabbitMQ.Summary
While Zend Server’s Job Queue is not the only way to handle queues and parallel processing in PHP, it is an extremely straight-forward solution backed by “The PHP Company” and is very easy to start using. And with the growing success of Zend’s PHPCloud adoption of Job Queue should become even wide-spread. If you want to view the example code from this article in its entirety, you can find it on GitHub. Image via Varina and Jay Patel/ShutterstockFrequently Asked Questions (FAQs) about Zend Queue
What is the primary function of Zend Queue?
Zend Queue is a component of the Zend Framework that provides a simple API for various queuing systems. It allows developers to create, manage, and process queues of data or tasks asynchronously. This means that tasks can be executed in the background, improving the performance and user experience of web applications. It supports multiple backends such as Array, SQLite, and others.
How does Zend Queue improve the performance of web applications?
Zend Queue improves the performance of web applications by allowing tasks to be processed asynchronously. This means that tasks can be executed in the background, without blocking the main thread of execution. This can significantly improve the responsiveness of a web application, as users do not have to wait for tasks to complete before they can continue interacting with the application.
How can I create a new queue in Zend Queue?
To create a new queue in Zend Queue, you can use the createQueue
method. This method takes two parameters: the name of the queue and the timeout. The timeout parameter is optional and defaults to null. Here is an example:$queue = new Zend_Queue('Array', array());
$queue->createQueue('myQueue');
How can I add a message to a queue in Zend Queue?
To add a message to a queue in Zend Queue, you can use the send
method. This method takes a single parameter: the message to be added to the queue. Here is an example:$queue = new Zend_Queue('Array', array());
$queue->send('myMessage');
How can I process messages from a queue in Zend Queue?
To process messages from a queue in Zend Queue, you can use the receive
method. This method retrieves a set of messages from the queue for processing. Here is an example:$queue = new Zend_Queue('Array', array());
$messages = $queue->receive();
How can I delete a queue in Zend Queue?
To delete a queue in Zend Queue, you can use the deleteQueue
method. This method takes a single parameter: the name of the queue to be deleted. Here is an example:$queue = new Zend_Queue('Array', array());
$queue->deleteQueue('myQueue');
How can I check if a queue exists in Zend Queue?
To check if a queue exists in Zend Queue, you can use the isExists
method. This method takes a single parameter: the name of the queue to check. Here is an example:$queue = new Zend_Queue('Array', array());
$exists = $queue->isExists('myQueue');
How can I count the number of messages in a queue in Zend Queue?
To count the number of messages in a queue in Zend Queue, you can use the count
method. This method returns the number of messages in the queue. Here is an example:$queue = new Zend_Queue('Array', array());
$count = $queue->count();
How can I clear all messages from a queue in Zend Queue?
To clear all messages from a queue in Zend Queue, you can use the purge
method. This method removes all messages from the queue. Here is an example:$queue = new Zend_Queue('Array', array());
$queue->purge();
How can I set the timeout for a queue in Zend Queue?
To set the timeout for a queue in Zend Queue, you can use the setTimeout
method. This method takes two parameters: the name of the queue and the timeout in seconds. Here is an example:$queue = new Zend_Queue('Array', array());
$queue->setTimeout('myQueue', 30);
Alex Stetsenko is a software engineer focused on architecture and development of high-load online system for real-time electricity metering and energy management. His primary concentration is PHP, MySQL, and JavaScript. In his spare time, Alex also ventures into Android development, Ruby on Rails, MongoDB and blogging at stetsenko.net.