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

真面目なネタは他の方に任せて、
ちょっと違うアイディアになりそうなものを紹介します

# 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** は先に入れておいてください

```bash
$ git clone git://github.com/zeromq/libzmq.git
$ cd libzmq/
$ ./autogen.sh
$ ./configure && make
$ make install
```

## php-zmq
```bash
$ git clone git://github.com/mkoppanen/php-zmq.git
$ cd php-zmq/
$ phpize
$ ./configure && make
$ make install
```

iniにzmq.soを追記してあげれば完了です
~~ちなみにこのエクステンションは残念ながらIDEで補完が効かないはずです~~
最新版のPHPStormでは補完が効くようです(他のIDEはわかりません)
もし補完が効かない方がいましたら、
packagistに補完のためのファイルを置いてくれている方がいますので
これを入れると手っ取り早いです

```json:composer.json
"require": {
    "php": ">=5.4.0",
    "ext-zmq": "*",
    "moriony/php-zmq-stubs": "dev-master"
}
```

では早速hello worldから
コンソールアプリとして送信側と受信側を用意します
まずは送信

```php: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](http://zeromq.org/)

zeromqは途切れたりプロセスが死んだ場合でも再接続するまで待ち続けるので、
このソースを実行するとレシーブするまで待ち続けますが
すでにメッセージは送られた状態になります

```bash
$ php client.php
```

続いて受信
送信側が待ってる状態なので**world** を返してあげます
コンソールはそれぞれ別のタブとかでやって下さい

```php: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");
}
```

```bash
$ php server.php
```
送信側のコンソールにworldが表示されているはず！
これがベーシックな流れです
ただのworldだった・・

# pubsubやってみよう
イベント駆動型のベーシックなものの一つ、pubsubを実装してみましょう
pubsubは皆さんご存知だと思いますが、
出版購読型モデルというそれらしい名前の仕組みで、
publisherが何かイベントを起こすとsubscriberが反応する仕組みを指します
redisのpubsubなどもまさにこの仕組みです
redisなどは使わずにzeromqを通じて実装します
まずはpublisher

```php: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

```php: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個くらいタブ等開いてコンソールで実行してみましょう

```bash
$ php subscriber.php
```

そしてpublisherを起動します

```bash
$ php publisher.php
```
5秒おきに全subscriberにメッセージが配信されて表示されているはず　です
publisherの対象チャンネルを変更するとsubscriberには配信されなくなります
ちょっと面白くなってきた所で、ここで他の言語と簡単に連携させてみます

# http/pubsub/node.js
ブラウザからアクセスして、ブラウザからpublisherへイベントを送信して、
node.jsで実装したsubscriberに配信します

```php: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は先ほどのやつを利用して少しいじります

```php: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のライブラリがあるので簡単です

```json:package.json
{
  "devDependencies": {
    "zmq": "*"
  }
}
```

```bash
$ npm install
```

サンプルまんまです

```js: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なりでアクセスしてみましょう

```bash 
$ node subscribe.js
$ php publisher.php
```

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

こんな感じでzeromqを利用して簡単に実装する事が出来ます
他のメッセージキューを利用してもほとんど同じはずなので
好みで色んなものを使って実装してみてください