キュー方式のジョブの作成とスケジューリング

キュー方式のジョブではジョブによるオペレーションをキューに登録することができます。ジョブはしばしば時間のかかるメモリを消費するオペレーションに使われます。ジョブの一部分を負荷の少ないタスクに分解することができれば、キュー方式のジョブはその問題への理想的な解決手段となります。一度にサブセットのみを処理することでメモリーの消費を抑え、問題なくジョブを完了させることができます。

キュー方式のジョブを作成する

キュー方式のジョブは異なるクラスを継承し、いくつかの異なるメソッドを実装し、concrete5の \Concrete\Core\Foundation\Queue クラスを使用します。

正しいクラスを継承する

このジョブについて考えてみましょう。

class TestJob extends \Concrete\Core\Job\Job
{

    public function getJobName()
    {
        return t("Test Job");
    }

    public function getJobDescription()
    {
        return t("We will make this job queueable for better performance.");
    }

    public function run()
    {
        $list = new \Concrete\Core\Page\PageList();
        $list->ignorePermissions();
        $pages = $list->getResults();
        $processed = 0;
        foreach($pages as $page) {
            $page->setAttribute('test_attribute', 'Test!');
            $processed++;
        }
        return t('%s pages processed', $processed);
    }

}

このジョブの目的は、サイト内の全てのページを(ジョブはしばしばログインを経ずに実行されるため権限をチェックせずに)取得し、ハンドル "test_attribute" の属性に "Test!" というコンテンツを登録するだけの、コンセプト実装です。処理したページの総数を返しています。

しかし、何千ページもあるサイトの場合にはどうなるでしょうか?このジョブは簡単にタイムアウトしてしまうでしょう。このような場合こそ、キュー形式の完璧な出番です。

まず、 \Concrete\Core\Job\Job の代わりに QueueableJob クラスを継承します。

class TestJob extends \Concrete\Core\Job\QueueableJob

QueueableJob クラスは抽象クラスですので、いくつかのメソッドを実装する必要があります。

abstract public function start(\ZendQueue\Queue $q);
abstract public function finish(\ZendQueue\Queue $q);
abstract public function processQueueItem(\ZendQueue\Message $msg);

これらのメソッドは Zend Framework の Queue クラスによって実行されます。メッセージと共にキューを送信する start メソッド、ジョブが完了した際のクリンナップを行う finish メソッド、各キューの処理を行う processQueueItem メソッドを作成しましょう。まず、start メソッドを作成します。

public function start(\ZendQueue\Queue $q)
{
    $list = new \Concrete\Core\Page\PageList();
    $list->ignorePermissions();
    $results = $list->executeGetResults();
    foreach($results as $queryRow) {
        $q->send($queryRow['cID']);
    }
}

このメソッドの最初の部分は見覚えがあるでしょう。PageList インスタンスを作成し、権限を無視しています。しかし、その続きはどうでしょうか?全てのページオブジェクトを取得する(PageList getResults() メソッドの通常の機能)代わりに、executeGetResults() メソッドを使っています。このメソッドはクエリーを実行しつつ、それぞれのオブジェクトは取得せず、データベースから取得した生のデータを返します。これは正しい方法です。なぜなら、start メソッドはキューに登録する全てのデータを抽出するため可能な限り処理を軽量化する必要があるからです。そのため、クエリーを実行し、システムからページのIDのリストを取得しています。次に全てのページIDをキューに送っています。

次に、processQueueItem を実装しましょう。

public function processQueueItem(\ZendQueue\Message $msg)
{
    $page = \Page::getByID($msg->body);
    $page->setAttribute('test_attribute', 'Test!');
}

このメソッドは非常に単純です。各キューで $msg として表される Message オブジェクトから body を取得します。start() メソッド内で $q->send() を用いて各キューに送信したデータが自動的に $msg->body に登録され、processQueueItem 内でアクセスできるようになります。データベースにはシリアライズされたオブジェクトとして保存されますので、シンプルなスカラー値以外のものも格納可能ですが、今回保存したのはページIDですので、ページIDが取得できる値です。 $msg オブジェクト内のページIDからページオブジェクトを作成し、ページの属性を登録しています。

最後に、finish() メソッドを実装します。finish() メソッドからキューについての情報を取得できないため、何件処理されたのかの実際のデータを取得することは困難です。しかし、多くの場合そのデータを返す必要はないでしょう。

public function finish(\ZendQueue\Queue $q)
{
    return t('All pages processed');
}

以上で、ジョブのキュー形式への変更が完了し、50ページから5000ページにスケールすることができました。

Scheduling a Queueable Job

Scheduling a Queueable Job works differently than a regular job. A regular Job, if it has as job ID of 1, would be accessed this way in a headless system:

http://www.yoursite.com/index.php/ccm/system/jobs?auth=authenticationHash&jID=1

Where authenticatioHash is equal to the authentication hash displayed in the Dashboard Jobs user interface. Typically something like wget or curl would be used to hit this URL periodically, running the job in the background of a site.

If this is done with a QueueableJob, the entire Job will be run all at once. This is for backward compatibility, and so that we never have a situation where someone thinks they're running a job and it isn't running. But what if you want to schedule a Queueable Job to run periodically, but still have it operate in batches like it does from the concrete5 Jobs interface? This requires a different approach. Instead of hitting the above URL, schedule two Jobs to run. The first is the Job URL for this Queueable Job:

http://www.yoursite.com/index.php/ccm/system/jobs/run_single?auth=authenticationHash&jID=1

Schedule this for when you'd like this Job to begin running.

Next, schedule the Job watcher process:

http://www.yoursite.com/index.php/ccm/system/jobs/check_queue?auth=authenticationHash

Schedule this to run frequently. This process simply checks any queues in the system and processes the next X items in the queue. If the queue is empty the process exits quietly. Got multiple jobs you'd like to Queue? Schedule them as above, and schedule a single run of the check_queue script. It will work for multiple Queueable Jobs.

That's it! Now you can have the benefits of Queueable Jobs in a scheduled, headless environment.

原文:Creating & Scheduling a Queueable Job