🍃このブログは移転しました。
3秒後、自動的に移動します・・・。

NodeのExpressにSocket.IOのアプリを統合する

ここ最近はNode.jsを勉強中で、Socket.IOについては一通りさわれたと思うので、今度はExpressをさわってます。

せっかくなので以前のSocket.IOのチャットアプリをExpressにのせて、以降のNodeの勉強をExpress上でやることにしました。
で、ここからがメモの本題。

Expressを普通にインストールしてSocket.IOを使うサンプルは結構ありますが、それらをそのまま参考にしてるとapp.jsがどんどん肥大化していきます。

なんの根拠もない個人的な理由ですが・・、

  • app.jsはサーバーを起動する部分と各種ルーティング機能への橋渡しなど、必要最低限にしたい
  • 他にサンプルを作ったときも、うまく結合できる

ので、コードを出来る限り分割することにしました。

正解・最適解かはわかりませんが、一応達成できたのでその記念のメモです。

Expressらしい使い方はまだまだ出来てないので、そこにたどり着くまでの環境整備的な内容ですね。

前提

今回のメモの内容の環境設定です。

  • 単一のドメイン以下で実装
  • URLに対する一旦のアクセスはNginxで受け、状況に応じてNodeで処理
  • Nginxで配布できる静的ファイルはそのまま出力
  • Nodeで処理するものは全てExpressで処理
  • Socket.IOは他のアプリでも利用できることを想定
  • バージョンは、Nodeは0.8.16、Expressは3.0.5、Socket.IOは0.9.13を使用

ちなみに、インストールした時点のapp.jsはserver.jsにリネームしてます。

Nginxの設定

virtual.conf

私の環境では、/etc/nginx/conf.d/virtual.conf です。

# server_nameとか諸々・・・

location / {
  if ( -f $request_filename ) {
      break;
  }

  if ( -f $request_filename/index.html ) {
      rewrite (.*) $1/index.html break;
  }

  if ( !-f $request_filename ) {
      proxy_pass    http://127.0.0.1:9999;
      break;
  }
}

# その他諸々・・・

ポートは適当。

フォルダ構成

. <- このドメインにおけるnginxのドキュメントルート
├── server.js
├── node_modules
│   └── express@3.0.5, socket.io@0.9.13, ejs@0.8.3...
├── apps
│   └── socketIoChat.js
├── public
│   └── socket-io-chat
│       ├── chat.js
│       └── style.css
├── routes
│   └── main.js
└── views
    ├── index.html
    └── socketIoChat.html

各フォルダ

説明不要かと思いますが一応。

  • server.js: Nodeサーバー起動用にシンプルに
  • node_modules: 随時ローカルインストールするので割愛
  • apps: アプリごとに逃したいコードはこちら
  • public: cssや画像、クライアントサイドのjsなど
  • routes: ルーティング用(必要に応じて分割予定)
  • views: いわゆるテンプレ

Expressの設定

/* Require / Use */
var express = require('express'),
http = require('http'),
path = require('path'),
routes = require('./routes/main');

/* Express settings */
var app = module.exports = express();
app.configure(function() {
  app.set('port', process.env.PORT || 9999);
  app.set('views', __dirname + '/views');
  app.set('view engine', 'ejs');
  app.use(express.favicon());
  app.use(express.logger('dev'));
  app.use(express.bodyParser());
  app.use(express.methodOverride());
  app.use(app.router);
  app.use(express.static(path.join(__dirname, 'public')));
  // Use .html as view engine.
  app.engine('html', require('ejs').renderFile);
});


/* Env settings */
app.configure('development', function() {
  app.use(express.errorHandler({
    dumpExceptions: true,
    showStack: true
  }));
});
app.configure('production', function() {
  app.use(express.errorHandler());
});

/* Url routings */
app.get('/', routes.index);
app.get('/socket-io-chat', routes.socketIoChat);

/* Create server */
var server = http.createServer(app).listen(app.get('port'), function() {
  console.log("Express server listening on port " + app.get('port'));
});

/* Socket.IO */
var io = require('socket.io').listen(server);
app.set('io', io);

/* For /socket-io-chat */
require('./apps/socketIoChat');

.ejsではなく.htmlをviewに使う

app.engine('html', require('ejs').renderFile);

requireするモジュール側で、appを参照する

var app = module.exports = express();

Express x Socket.IO

var server = http.createServer(app).listen(app.get('port'), function() {
  console.log("Express server listening on port " + app.get('port'));
});

// ...

var io = require('socket.io').listen(server);

検索して見つかったサンプルと違うのはこれくらいかな?

Socket.IOのコードを逃がす

// server.js

// ...

// app自体を退避
var app = module.exports = express();

// ...

var io = require('socket.io').listen(server);
// ioをapp.setで一旦退避
app.set('io', io);

/* For /socket-io-chat */
require('./apps/socketIoChat');

これである程度見通しの良いままにしておいて・・

// ./apps/socketIoChat.js

// app自体を再定義したら参照可能に!
var app = module.parent.exports,
// 退避したioをappから参照
io = app.get('io');

var socketIoChatSocket = io.of('/chat').on('connection', function(client) {

// ...

	client.emit('connected');

// ...

});

これで違うSocket.IOアプリを載せたとしても、server.jsは綺麗なままにできる気がする。
この内部のコードは、以前にサンプルを載せてるので割愛です。(現行ちょっとだけ変わってるけど・・。)

参考:NodeとSocket.IOで作るルーム機能つきチャットのサンプル - console.lealog();

以上、忘れないように。
これからもっとNodeでいろいろやりますよ。
Expressの掘り下げと、fsモジュール関連とかスクレイピングとかもしてみたい。