SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

DeveloperZine(デベロッパージン)- エンジニアの意思決定を支える技術情報メディア ProductZine

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

Zend Framework入門

PHPアプリでメッセージキューサーバを活用する - Zend_Queue -

Zend Frameworkによる実践的なPHPアプリケーション開発 33

Stompプロトコル

 StompプロトコルはApache ActiveMQなどのメッセージキュー管理サーバで利用されている、メッセージをやりとりするためのプロトコルです。ここで、Stompプロトコルで通信するためのスクリプトを書く方法について、少し触れておきます。

 前回述べたとおり、ActivemqアダプタはStompプロトコルでメッセージキューサーバと通信していますが、この処理は内部ではZend_Queue_Stomp関係のクラスを利用して実装されています。

 Zend_Queue_Stomp関係のクラスにはZend_Queue_Stomp_ClientクラスとZend_Queue_Stomp_Frameクラスが存在します。メッセージをやりとりするサーバとクライアントの接続の単位を管理するのがZend_Queue_Stomp_Clientクラスで、Stompプロトコルでの通信の単位であるフレームに対応するのがZend_Queue_Stomp_Frameクラスです。

 Zend_Queue_Stomp関係のクラスを利用する手順は

  • (1)クライアントの初期化
  • (2)Stompプロトコルによるサーバとの通信

 となります。ここではローカルホストのポート「61613」に対してStompプロトコルでメッセージを登録する例を見てみます。

[リスト3]Stompプロトコルでのメッセージのやりとり(stomp.php)
<?php
require_once 'Zend/Queue/Stomp/Frame.php';
require_once 'Zend/Queue/Stomp/Client.php';

/* (1)クライアントの初期化 */
$client = new Zend_Queue_Stomp_Client('tcp', 'localhost', '61613');

/* (2)サーバとの通信 */
/* (2-1)接続 */
$connect = $client->createFrame();
$connect->setCommand('CONNECT');
$response = $client->send($connect)->receive();

/* (2-2)メッセージの登録 */
$frame = $client->createFrame();
$message = "Hello World!!";
$frame->setCommand('SEND');
$frame->setHeader('destination', '/queue/codezine_queue');
$frame->setHeader('content-length', strlen($message));
$client->send($frame);

 このように(1)Zend_Queue_Stomp_Clientクラスのコンストラクタでサーバへの接続方法について指定した後(2)サーバに送りたい情報を順番にStompプロトコルで送信して行きます。ここでは(2-1)サーバとの接続を確立した後(2-2)サーバに「Hello World!!」のメッセージを登録しています。

アダプタの自作

 既存のアダプタが準備されていないメッセージキューを利用するためには、自分でアダプタを作成する必要があります。似ているアダプタがある場合には、そのアダプタを継承/拡張するのが良いでしょう。

 全く新しいアダプタを作成する場合には、Zend_Queue_Adapter_AdapterAbstractクラスを継承して、必要な機能を実装していきます。ここではファイルにテキストでメッセージを格納するメッセージキューを例に説明します。なお、話を単純にするため単一のキューのみを扱えるものとし、またメッセージが取得された段階でキューから取り除かれるとします。

 まず、実装する必要のあるメソッドの一覧を見てみます。

アダプタで実装する必要のあるメソッド
分類 メソッド 機能
基本 __construct コンストラクタ
キュー create キューを作成
  delete キューを削除
  isExists キューが存在するかを取得
  getQueues キューの一覧を取得
  count キュー内にあるメッセージを取得
メッセージ send メッセージを追加
  receive メッセージを取得
  deleteMessage メッセージを削除
※注意

 今回は単一のキューのみを扱うため、上の表の「キューの管理」にあるメソッドは実装しません。また、deleteMessageメソッド/countメソッドも実装しません。また、キューを記録しておくファイルの名前は、オプションの「driverOptions.file」で与えることにします。

 では、まずコンストラクタから見ていきます。

[リスト4]コンストラクタ(File.php)
class Zend_Queue_Adapter_File extends Zend_Queue_Adapter_AdapterAbstract
{
    public function __construct($options, Zend_Queue $queue = null)
    {
        /* (1)AdapterAbstract のコンストラクタを呼ぶ
         *  この中で $options の解析が行われる
         */
        parent::__construct($options, $queue);

        /* (2)ここでキューの初期化 */
    }

 まず(1)コンストラクタでは親クラスのコンストラクタを呼び出しています。この中では、引数の$option変数を解析し、アダプタのオプションを$this->_options['adapterOptions']に、メッセージキュー管理システムへの引数を$this->_options['driverOptions']へ設定しています。

 また、キューの初期化等の作業が必要な場合には(2)で行います。今回のサンプルでは特に何も行いませんが、例えばDbアダプタではデータベースへの接続はここで行なっています。

 次にメッセージの登録と取得です。

[リスト5]sendメソッド(File.php)
public function send($message, Zend_Queue $queue=null)
{
    /* ファイルを追記用に開く */
    $fp = fopen($this->_options['driverOptions']['file'], 'a');
    /* ファイルをロック */
    flock($fp, LOCK_EX);
    /* メッセージの追記 */
    fputs($fp, $message."\n");
    /* ロックの開放 */
    flock($fp, LOCK_UN);
    /* ファイルを閉じる */
    fclose($fp);
}

 メッセージの登録は簡単で、ファイルにメッセージ追記するだけです。他のプロセスがファイルへ同時にアクセスしないようにするため、排他的なロックをかけています。

 メッセージの取得も基本的に同じような処理になりますが

  • 取得したメッセージを削除したい
  • 取得したメッセージを正しい型で返さないといけない

 ため、処理がやや面倒になっています。

[リスト6]receiveメソッド(File.php)
public function receive($maxMessages = null, $timeout = null, Zend_Queue $queue = null)
{
    if ($queue === null) {
        $queue = $this->_queue;
    }

    /* (1)ファイルを開く */
    $count = 0;
    $fp = fopen($this->_options['driverOptions']['file'], 'r');
    $fp_new = fopen('queue_new', 'w');
    flock($fp, LOCK_EX);
    flock($fp_new, LOCK_EX);

    /* (2)ファイルの各行を取得 */
    while (!feof($fp)) {
         $line = fgets($fp);
         if ($count < $maxMessages) {
           /* (2-1)取得上限に達していないならメッセージを取得 */
           $count++;
           $message = rtrim($line, "\n");
           /* (2-2)メッセージの準備 */
           $msg['body'] = $message;
           $data[] = $msg;
         } else {
           /* (2-3)取得上限に達しているならそのまま残す */
           fputs($fp_new, $line);
         }
    }
    /* (3)ロック等の開放 */
    /* (3-1)一時的なロック */
    $fp_tmp = fopen('queue_tmp', 'w');
    flock($fp_tmp, LOCK_EX);

    /* (3-2)ロック等の開放 */
    flock($fp, LOCK_UN);
    flock($fp_new, LOCK_UN);
    fclose($fp);
    fclose($fp_new);

    /* (4)残すメッセージを含むファイルに旧ファイルを置き換える */
    rename('queue_new', $this->_options['driverOptions']['file']);
    flock($fp_tmp, LOCK_UN);
    fclose($fp_tmp);

    /* (5)返り値の準備 */
    $options = array(
        'queue'        => $queue,
        'data'         => $data,
        'messageClass' => $queue->getMessageClass(),
    );

    $classname = $queue->getMessageSetClass();
    if (!class_exists($classname)) {
        require_once 'Zend/Loader.php';
        Zend_Loader::loadClass($classname);
    }
    return new $classname($options);
}

 このうち(1)~(4)までがメッセージの取得と取得済メッセージの削除、(5)が返り値の準備になっています(図1_1、図1_2)。

図1_1 メッセージの取得の流れ 1
図1_1 メッセージの取得の流れ 1
図1_2 メッセージの取得の流れ 2
図1_2 メッセージの取得の流れ 2

 (1)では2つのファイルを開いていますが、これはそれぞれメッセージを読み込むためのファイル($fp)と、キューに残しておくためのメッセージを記録するためのファイル($fp_new)です。(2)では、メッセージの記録されているファイルを一行一行見て、メッセージ取得数の上限に達していない場合には(2-1)メッセージを取得して(2-2)で配列$msgに必要な情報('body')を記録しています。

 一方で、メッセージ取得数の上限に達している場合には(2-3)でキューに残すメッセージとして$fp_newに記録しています。

 (4)ではキューに残すメッセージが記録されたファイルでメッセージキューが記録されているファイルを置き換えています。このことで、実質、取得されたメッセージが削除されます。

 (5)では、得られたメッセージをreceiveメソッドの返り値としてふさわしいように、Zend_Queue_Message_Iteratorクラスのオブジェクトに格納しています(このreceiveメソッドの返り値については後から少し説明します)。

次のページ
おわりに

この記事は参考になりましたか?

Zend Framework入門連載記事一覧

もっと読む

この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるLaravel実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 風田 伸之(カゼタ ノブユキ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。 2026年時点での登録メンバは約50名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

CodeZine(コードジン)
https://codezine.jp/article/detail/5744 2011/02/28 14:00

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー