Edited at
PHPDay 21

PHPでメッセージ送受信(zeromq)

More than 3 years have passed since last update.

真面目なネタは他の方に任せて、

ちょっと違うアイディアになりそうなものを紹介します


zeromqって何だ?

いわゆるメッセージングが可能になるミドルウェアです

メッセージキューにもいろいろあり、

RabbitMQは利用している方も多いのではないでしょうか?

他にもActiveMQ、zeromqなどがあります

今回はその中でも軽量と言われるzeromqを使って

PHPからの送受信、他の言語との通信などにチラっと触れます

zeromqを通じて言語を問わずシステムを構築するのであれば、

phpだったり、Cだったり、node.jsだったり、

socket.ioの様なものを使ったり等の直接的な連携というより、

イベント駆動による連携になるので、

場合によっては作りたいものをシンプルに実装できる事もあります


php meet zeromq

phpでzeromqと言えば、あまり使っているライブラリなどは見かけませんが、

node.jsライクなReactPHPが結構活用していて、pubsubの実装に使っていたりするので

見てみると面白いかもしれません

普段からReactPHP使ってる方は ああ、あれね というネタです

折角なので突っ込んでみて触ってみましょう

導入方法は色々ありますので好きな方法でインストールして下さい


libzmq

libtool autoconf automake は先に入れておいてください

$ git clone git://github.com/zeromq/libzmq.git

$ cd libzmq/
$ ./autogen.sh
$ ./configure && make
$ make install


php-zmq

$ git clone git://github.com/mkoppanen/php-zmq.git

$ cd php-zmq/
$ phpize
$ ./configure && make
$ make install

iniにzmq.soを追記してあげれば完了です

ちなみにこのエクステンションは残念ながらIDEで補完が効かないはずです

最新版のPHPStormでは補完が効くようです(他のIDEはわかりません)

もし補完が効かない方がいましたら、

packagistに補完のためのファイルを置いてくれている方がいますので

これを入れると手っ取り早いです


composer.json

"require": {

"php": ">=5.4.0",
"ext-zmq": "*",
"moriony/php-zmq-stubs": "dev-master"
}

では早速hello worldから

コンソールアプリとして送信側と受信側を用意します

まずは送信


client.php

$queue = new \ZMQSocket(new \ZMQContext(), \ZMQ::SOCKET_REQ);

$queue->connect("tcp://127.0.0.1:5554");
echo $queue->send("hello")->recv();

通信には127.0.0.1:5554を指定しましたがお好みでどうぞ

各クラスなどは公式のリファレンスをみるといいです

(php.net、またはzeromqの方 基本的にCと同じです)

zeromq

zeromqは途切れたりプロセスが死んだ場合でも再接続するまで待ち続けるので、

このソースを実行するとレシーブするまで待ち続けますが

すでにメッセージは送られた状態になります

$ php client.php

続いて受信

送信側が待ってる状態なのでworld を返してあげます

コンソールはそれぞれ別のタブとかでやって下さい


server.php

$server = new \ZMQSocket(new \ZMQContext(), \ZMQ::SOCKET_REP);

$server->bind("tcp://127.0.0.1:5554");

while (true) {
$request = $server->recv();
$server->send("world\n");
}


$ php server.php

送信側のコンソールにworldが表示されているはず!

これがベーシックな流れです

ただのworldだった・・


pubsubやってみよう

イベント駆動型のベーシックなものの一つ、pubsubを実装してみましょう

pubsubは皆さんご存知だと思いますが、

出版購読型モデルというそれらしい名前の仕組みで、

publisherが何かイベントを起こすとsubscriberが反応する仕組みを指します

redisのpubsubなどもまさにこの仕組みです

redisなどは使わずにzeromqを通じて実装します

まずはpublisher


publisher.php

$publisher = new \ZMQSocket(new \ZMQContext(), \ZMQ::SOCKET_PUB);

$publisher->bind("tcp://127.0.0.1:5563");

while (true) {
$publisher->send("FromPub", \ZMQ::MODE_SNDMORE);
$publisher->send("hello");
sleep(5);
}


さっきとそんなに大きく変わりませんが、

\ZMQ::SOCKET_PUB でpublishするぞと指定して、

対象チャンネルの FromPub が追加されたくらいです

一瞬で終わると面白くないので、

whileでsleepしながら実行する様にしました

楽しみを取っておく為にもここではまだスクリプトを実行しない様にしておきましょう

まだ待つんだ・・!

次にsubscriber


subscriber.php

$subscriber = new \ZMQSocket(new \ZMQContext(), \ZMQ::SOCKET_SUB);

$subscriber->connect("tcp://127.0.0.1:5563");
$subscriber->setSockOpt(\ZMQ::SOCKOPT_SUBSCRIBE, "FromPub");
while (true) {
$address = $subscriber->recv();
$contents = $subscriber->recv();
printf ("[%s] %s%s", $address, $contents, PHP_EOL);
}

指定したチャンネルのみを受信する様に実装しただけです

subscriberはいくつあっても構わないので

10個くらいタブ等開いてコンソールで実行してみましょう

$ php subscriber.php

そしてpublisherを起動します

$ php publisher.php

5秒おきに全subscriberにメッセージが配信されて表示されているはず です

publisherの対象チャンネルを変更するとsubscriberには配信されなくなります

ちょっと面白くなってきた所で、ここで他の言語と簡単に連携させてみます


http/pubsub/node.js

ブラウザからアクセスして、ブラウザからpublisherへイベントを送信して、

node.jsで実装したsubscriberに配信します


http.php

$socket = new \ZMQSocket(new \ZMQContext(), \ZMQ::SOCKET_REQ);

$socket->connect("tcp://127.0.0.1:5554");

$result = $socket->send("httpリクエスト")->recv();
echo $result;


publisherは先ほどのやつを利用して少しいじります


publisher.php

$context = new \ZMQContext();

$server = new \ZMQSocket($context, \ZMQ::SOCKET_REP);
$server->bind("tcp://127.0.0.1:5554");
while (true) {
$request = $server->recv();
if(!is_null($request)) {
$server->send("get message");
break;
}
}

$publisher = new \ZMQSocket($context, \ZMQ::SOCKET_PUB);
$publisher->bind("tcp://127.0.0.1:5563");
while (true) {
$publisher->send("FromPub", ZMQ::MODE_SNDMORE);
$publisher->send($request);
sleep(5);
}


受信したよとブラウザに返答して、

そのメッセージをそのままsubscriberに送信します

次にsubscriberをnode.jsで実装!

zmqのライブラリがあるので簡単です


package.json

{

"devDependencies": {
"zmq": "*"
}
}

$ npm install

サンプルまんまです


subscriber.js

var zmq = require('zmq'),

subscriber = zmq.socket('sub');

subscriber.connect('tcp://127.0.0.1:5563');
subscriber.subscribe('FromPub');
console.log('Subscriber connected to port 5563');

subscriber.on('message', function(topic, message) {
console.log(
'received a message related to:',
topic.toString('utf8'),
'containing message:',
message.toString('utf8')
);
});


それぞれ起動させて、ブラウザなりcurlなりでアクセスしてみましょう

$ node subscribe.js

$ php publisher.php

node.js側にブラウザからのメッセージが表示されているはず!

こんな感じでzeromqを利用して簡単に実装する事が出来ます

他のメッセージキューを利用してもほとんど同じはずなので

好みで色んなものを使って実装してみてください