二回目の微妙な登場でございます。
#websocket push!
websocketの代表と言えばchatでしょうか?
laravel4でのチャット実装のそのまんまなサンプルは
laravel4 cookbook
にありますので、
そちらを参考にしていただければと思います。
laravel4パッケージにしたLatchetもありますが、
ratchetを使います。
今回はlaravel4で実装するwebsocket pushについてです
pushをご存知でない方が一番分かりやすいのは、
iPhoneやandroidでニュースやメッセージを受信したときに通知されるnotificationの
web版だと思ってください。
つまり、アクセスしている人にサーバから送信する事ができます。
node.jsで広告やコンテンツを一斉に配信したり、
ブラウザゲーム系にも活用されている方は多いかもしれません。
Ratchetとzmqを使って実装します。(といってもリファレンス通りですが!)
Ratchet
まずサーバpushを実装するにあたっていくつか必要なものがありますので、
makeします。
(mac環境での構築方法なので他のOSの方はそれ用にあわせてください)
####libzmq
$ git clone git://github.com/zeromq/libzmq.git
$ cd libzmq/
$ ./autogen.sh
$ ./configure && make
$ make install
*errorが出る場合はmacPortsでlibtool autoconf automakeをinstallしてください。
####php-zmq
$ git clone git://github.com/mkoppanen/php-zmq.git
$ cd php-zmq/
$ phpize
$ ./configure && make
$ make install
re2cあたりがエラー出る場合は同様にmacPortsでzmq.soをiniに忘れずに追記してください。
php -mで確認してください!
あとはいつもと同じです
"require": {
"laravel/framework": "4.0.*",
"cboden/Ratchet": "0.3.*",
"react/zmq": "0.2.*"
}
####push実装
namespace Wamp;
use Ratchet\ConnectionInterface;
use Ratchet\Wamp\WampServerInterface;
class Pusher implements WampServerInterface
{
protected $subscribedTopics = array();
public function onSubscribe(ConnectionInterface $conn, $topic)
{
if (!array_key_exists($topic->getId(), $this->subscribedTopics))
{
$this->subscribedTopics[$topic->getId()] = $topic;
}
}
/**
* @param string JSON'ified string we'll receive from ZeroMQ
*/
public function onBlogEntry($entry) {
$entryData = json_decode($entry, true);
if (!array_key_exists($entryData['cat'], $this->subscribedTopics)) {
return;
}
$topic = $this->subscribedTopics[$entryData['cat']];
$topic->broadcast($entryData);
}
public function onUnSubscribe(ConnectionInterface $conn, $topic) {
}
public function onOpen(ConnectionInterface $conn) {
}
public function onClose(ConnectionInterface $conn) {
}
public function onCall(ConnectionInterface $conn, $id, $topic, array $params) {
$conn->callError($id, $topic, 'You are not allowed to make calls')->close();
}
public function onPublish(ConnectionInterface $conn, $topic, $event, array $exclude, array $eligible) {
$conn->close();
}
public function onError(ConnectionInterface $conn, \Exception $e) {
}
}
onBlogEntryが受け、
サーバから接続しているブラウザに送信する様にします。
実際にはDBで配信時間を見ながら自動で送信したりという実装ですが、
書くと長くなるので省きます
####コマンド実装
use Illuminate\Console\Command;
use Symfony\Component\Console\Input\InputOption;
use Symfony\Component\Console\Input\InputArgument;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use React\Socket;
class WampPushCommand extends Command {
/**
* @var string
*/
protected $name = 'wamp:push';
/**
* The console command description.
* @var string
*/
protected $description = "boot wamp server";
/**
* @return void
*/
public function fire()
{
$loop = React\EventLoop\Factory::create();
$pusher = new Wamp\Pusher;
$context = new \React\ZMQ\Context($loop);
$pull = $context->getSocket(ZMQ::SOCKET_PULL);
$pull->bind('tcp://127.0.0.1:5555');
$pull->on('message', array($pusher, 'onBlogEntry'));
$webSock = new \React\Socket\Server($loop);
$webSock->listen(10000, '0.0.0.0');
$webServer = new Ratchet\Server\IoServer(
new Ratchet\Http\HttpServer(
new Ratchet\WebSocket\WsServer(
new Ratchet\Wamp\WampServer($pusher
)
)
), $webSock);
$this->info('wamp boot');
$this->info('Listening on port ' . 10000);
$loop->run();
}
}
この例は
port 5555から受け付けて、port 10000にpushを行います。
実際にはコマンドの引数でport指定する様にしましょう。(サンプルなので手抜き)
ここでいうWampとは、The WebSocket Application Messaging Protocolで、
windows環境をさすものではありませんYo
artisanに登録します。
$artisan->add(new WampPushCommand);
これでwebsocketの準備が整いました。
$ php artisan wamp:push
wamp boot
Listening on port 10000
上記の様に起動します。
次にブラウザです。
折角なのでデフォルトのhello.phpに書いてしまいましょう
リファレンスと同じ様にwhen.js, autobahn.jsにしときます。
<script src="/js/when.js"></script>
<script src="http://autobahn.s3.amazonaws.com/js/autobahn.min.js"></script>
<script src="//code.jquery.com/jquery-1.10.2.min.js"></script>
<script>
var conn = new ab.Session(
'ws://127.0.0.1:10000' , function() {
conn.subscribe('news', function(topic, data) {
console.log('New article published to category "' + topic + '" : ' + data.title);
$('.welcome').html(
'New article published to category "' + topic
+ '"<br /> title : ' + data.title
+ "<br /> article : " + data.article
);
});
}, function() {
console.warn('WebSocket connection closed');
}, {
'skipSubprotocolCheck': true
}
);
</script>
newsカテゴリーを購読して、受信したときにlaravel4ロゴを書き換えます。(わかりやすい!)
リソースでエントリーします
public function create()
{
$entryData = [
'cat' => 'news',
'title' => 'ニュース!',
'article' => 'laravel4.1 でましたよ',
'when' => time()
];
$context = new ZMQContext();
$socket = $context->getSocket(ZMQ::SOCKET_PUSH, 'my pusher1');
$socket->connect("tcp://localhost:5555");
$socket->send(json_encode($entryData));
}
先ほどのlaravel4のデフォルトページをそのまま表示させておき、
他のブラウザかタブからリソースを叩いて送ってみましょう。
####じゃーん
今回はリソース直叩きなので、普通のwebsocketと変わらないじゃん!と言われそうですが、
実際にはこれをサーバのみで実装します。
webでできるなんて、ちょっと気持ち悪いですね!!!
慣れるまでちょっと長い道のりですが、
実装できる様になると色んなものに使えると思います。
laravel4でwebsocketアプリケーションを実装する事で、
ブラウザからごにょごにょしたデータをnode.jsに送って解析して云々などしなくても、
用意されているものでユーザーデータだったり、色んな情報も簡単に実装する事ができます。
Illuminate万歳!
明日はatijust さんです