Hapi.js のREST APIが一通り動く ようになったので次はHapi.jsにSocket.IOサーバーを作成してみます。Using hapi.js with Socket.io にとても良いSocket.IOの解説があります。著者のMatt Harrison氏はManningでHapi.js in Action をMEAPで執筆中です。ブログが非常にわかりやすいので著書も期待できます。
リソース Hapi.jsとSocket.IOの使い方は以下のサイトを参考にして勉強します。
プロジェクト 適当なディレクトリにプロジェクトを作成します。リポジトリはこちら です。
$ cd ~/node_apps/docker-hapi-socketio $ tree -a -L 2 . ├── .dockerignore ├── .env ├── .gitignore ├── Dockerfile ├── README.md ├── app.js ├── docker-compose.yml ├── node_modules -> /dist/node_modules ├── npm-debug.log ├── package.json └── templates └── index.html
以下のバージョンのパッケージを使います。
~/node_apps/docker-hapi-socketio/package.json { "name" : "docker-hapi-socketio" , "description" : "docker-hapi-socketio" , "version" : "0.0.1" , "private" : true , "dependencies" : { "hapi" : "^8.6.1" , "socket.io" : "^1.3.5" , "handlebars" : "^3.0.3" , "dotenv" : "^1.1.0" }, "scripts" : {"start" : "node app.js" } }
サーバーサイド hapi serverのlisnterはhttp server hapi server(var server = new Hapi.Server();
)にconnectionを作成(server.connection({ port: 4000 });
)すると、内部で新しいNode.jsのhttp server(var listener = require('http').createServer(handler);
)が作成されます。このhttp serverインスタンスはlistenerプロパティ(var listener = server.listener;
)になります。
Socket.IOに渡すappもhttp server Socket.IOをrequireするときに渡すvar io = require('socket.io')(app);
のapp変数もhapi serverのlistenerと同様にhttp server(var app = require('http').createServer(handler);
)です。ただし以下のようにhapi serverにconnectionを作成して作成した8080ポートのhapi server.listener
(node http server
)をSocket.IOに渡すとセットアップ処理でnode http server
のrequestイベントのリスナーがすべて削除されてしまいます。
var Hapi = require ('hapi' )var server = new Hapi.Server();server.connection({ port : 8080 }); var io = require ('socket.io' )(server.listener);io.on('connection' , function (socket ) { console .log('connected' ); }); server.start();
API用のhttp serverとSocket.IO用のhttp server hapiはportを指定して内部で複数のnode http server
を起動することができます。下の例では1つのhapi serverでREST APIを8080ポートでLISTENするHTTPサーバーと、8080ポートでLISTENするSocket.IOサーバーの2つが起動しています。HTTPサーバーはいまのところ/
からindex.htmlのエントリポイントを提供するだけでREST APIのRouteは実装していません。
~/node_apps/docker-hapi-socketio/app.js 'user strict' ;var Hapi = require ('hapi' ), server = new Hapi.Server(), Path = require ('path' ); require ('dotenv' ).load();server.connection({ port : process.env.API_PORT, labels : ['api' ] }); server.connection({ port : process.env.SOCKETIO_PORT, labels : ['twitter' ] }); server.views({ engines: { html: require ('handlebars' ) }, path: Path.join(__dirname, 'templates' ) }); server.select('api' ).route({ method: 'GET' , path: '/' , handler: function (request, reply ) { reply.view('index' , { socketio_host : (process.env.PUBLIC_IP+':' +process.env.SOCKETIO_PORT)}); } }); var io = require ('socket.io' )(server.select('twitter' ).listener);io.on('connection' , function (socket ) { console .log('connected!' ); var tweet = {text : 'hello world!' }; var interval = setInterval (function ( ) { socket.emit('tweet' , tweet); }, 5000 ); socket.on('disconnect' , function ( ) { clearInterval (interval); }); }); server.start();
クライアントサイド クライアントサイドはSPAで実装します。HTTPサーバー(8000)がserveするindex.htmlがエントリポイントになります。index.htmlからSocker.IOサーバー(8080)がserveするsocket.io.js
のクライアントライブラリをロードしてSocket.IOサーバー(8080)に接続します。socket変数のconnect
とtweet
イベントにlistenerをアタッチします。
~/node_apps/docker-hapi-socketio/templates/index.html <!DOCTYPE html > <html lang ="ja" > <head > <meta charset ="utf-8" > <title > tweet</title > <script src ="//ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js" > </script > <script src ="http://{{socketio_host}}/socket.io/socket.io.js" > </script > <script > $(function ( ) { var socket = io.connect("http:// {{socketio_host }} "); socket.on("connect" , function ( ) { console .log("Connected!" ); }); socket.on("tweet" , function (tweet ) { console .log(tweet); $("#tweet" ).prepend(tweet.text + "<br>" ); }); }); </script > </head > <body > <div id ="tweet" > </div > </body > </html >
このindex.htmlはHandlebars.js のテンプレートです。{{socketio_host}}
は.env
に定義した環境変数をdotenv を使ってロードした値が入っています。
プログラムの実行 Docker Composeからhapiサービスをupします。ブラウザから.env
ファイルに定義したIPアドレスとポートに接続するとconnected!
のログが出力されます。
$ docker-compose up Recreating dockerhapisocketio_hapi_1... Attaching to dockerhapisocketio_hapi_1 hapi_1 | hapi_1 | > docker-hapi-socketio@0.0.1 start /app hapi_1 | > node app.js hapi_1 | hapi_1 | connected!
サーバーサイドでは5秒間隔でメッセージをemitしています。
~/node_apps/docker-hapi-socketio/app.js var tweet = {text : 'hello world!' };var interval = setInterval (function ( ) { socket.emit('tweet' , tweet); }, 5000 );
ブラウザサイドにも5秒間隔でメッセージがappendされていきます。