2012-06-15

Heroku や Google App Engine のさびれたアプリの初回アクセスを速くする方法

Heroku や Google App Engine などのWebアプリケーションを無料からホストしてくれるとてもありがたいサービスで、自分しか使わないデモアプリなどを置いている場合、久しぶりにアクセスすると、そのアクセスしたタイミングでやおらアプリケーションのインスタンスが立ち上がるのでしばらく(数秒程度)待たされます。

この動作があまり格好良くないので、何とかする方法を考えたところ、Webサイトの死活監視を無料で50サイトまで行ってくれるとてもありがたいサービス Uptime Robot を使って監視しておくと、定期的にアクセスしてくれるのでとても快適に動くようになります。ついでにダウンタイムも分かります。

もちろん世の中の全てのホストされているアプリをこのようにしてしまうとメモリ資源が無駄に必要になると予想されるので、良い子は監視対象とするのは必要な分だけにしましょう。

蛇足として Google App Engine では静的なファイルは Google CDN から配信されるのでアプリケーションがレスポンスを出すような URL を設定する必要があります。

2012-06-07

[Express] app.configure と app.use の使い方について

Express では app.configure を使うことで、NODE_ENV の本番環境 (production) 、開発環境 (development) の指定に応じて処理を分岐して設定を行うことができます。今回は具体例として、ミドルウエアを淡々と app.use(fn) で追加していく場合を考えます。

この時 app.use() が呼ばれる順番の通りに app の内部状態が設定されるので、production, development の両方に共通したミドルウエアを末尾の方に追加するには、適切なタイミングまで待ってから fn が呼ばれる app.configure(fn) を使う必要があります。app.configure も呼ばれた順番を尊重してコールバック関数を呼ぶことに要注意です。

また他にも、複数の場合に対応する app.configure(env1, env2, ..., fn) もOKです。NODE_ENV が存在しなかった場合のデフォルト値は development です (express/lib/http.js)。

var express = require('express'),
    app = express();

app.use(function (req, res, next) {
    console.log('1. before config');
    next();
});

app.configure('development', function () {
    app.use(function (req, res, next) {
        console.log('2. development');
        next();
    });
});

app.configure('production', function () {
    app.use(function (req, res, next) {
        console.log('2. production');
        next();
    });
});

app.configure('development', 'production', function () {
    app.use(function (req, res, next) {
        console.log('3. development/production');
        next();
    });
});

app.configure(function () {
    app.use(function (req, res, next) {
        console.log('4. after config');
        next();
    });
});

app.get('/', function (req, res, next) {
    console.log('5. get root');

    res.type('txt');
    res.send('ok');
});

app.listen(3000);

/* 実行例: http://localhost:3000/ をGETした場合

$ node server.js
1. before config
2. development
3. development/production
4. after config
5. get root

$ env NODE_ENV=production node server.js
1. before config
2. production
3. development/production
4. after config
5. get root

*/

2012/12/11 追記
app.configure は v3.0.0 で非推薦メソッドとなっているようです。今後は if (app.get('env') === 'development') などとして分岐すれば良いとのことです。

2012-06-03

[Express] リクエストハンドラの引数 next の使い方について

Node.js の Web Application Framework の一つである Express において、リクエストハンドラ第三引数の next の使い方について調べてみました。結局のところこれは一体何なのかというと、next の中身は次にリクエストを処理するべき関数を呼び出してくれる関数オブジェクトです。物好きな人用の説明なら、req, res の状態を持って次のリクエストハンドラに goto する continuation のイメージです。

実際にどのように使うかと言うと、何らかの理由でレスポンスを返さない場合に next() という感じで関数呼び出しを行います。引数として、自分のところで対処できないエラー (例外) が発生した場合はエラーオブジェクトを渡し、エラーはないけれど自分のところで対処するべきリクエストでない場合は引数なしで呼び出します。

エラーが発生した場合に適切に伝搬させるの例

var express = require('express'),
    app = express();

app.get('/error_sync', function (req, res, next) {
    next(new Error('sync error'));
    // この場合は throw new Error('sync error') でもOK
});

app.get('/error_async', function (req, res, next) {
    setTimeout(function () {
        next(new Error('async error'));
    }, 100);
});

app.listen(3000);

自分のところでは対処しないの例: リクエストのパラメータが3の倍数の場合 aho と返す。

var express = require('express'),
    app = express();

// 例えば http://localhost:3000/say/31 を開くと?

app.get('/say/:num', function (req, res, next) {
    var num = req.param('num');

    if (num % 3 === 0) {
        res.type('txt');
        res.send('aho: ' + num);
    } else {
        next();
    }
});

app.get('/say/:num', function (req, res, next) {
    var num = req.param('num');

    res.type('txt');
    res.send('not aho: ' + num);
});

app.listen(3000);

このコードを動作させるためには node.js に加えて express が必要なので、本体に同梱の npm コマンドを使って、同じディレクトリで npm install express として express をインストールする必要があります。

2012-06-02

[gimp] PNG 画像のファイルサイズを小さくするには

画面のキャプチャなどを JPEG で保存すると色が急激に変化するところの周りにもじゃっとしたノイズが乗るので通常 PNG で保存すると思いますが、何も考えず保存するとおそらくRGB各色8ビット(True Color) でアルファチャンネルなしの1ピクセルあたり24ビットのファイルが出来上がります。ネットも速くなったので通常これでクライアント側は困ることはないと思いますが、配信側の都合上転送量を削減したいなど極限まで絞りたいという場合は、もっと効率的なビット割り当てで同じような見た目になるインデックスカラーを検討してみる価値があります。

インデックスカラーとはピクセルごとにRGB値を記録するのではなく、あらかじめ色の目次(インデックス)を作ってから、各ピクセルでは目次の何番の色を置いたらよいかを記録する方式です。例えば256色インデックスカラーなら、log2 256 = 8 から、各ピクセルでは8ビットの割り当てで済みます。

GIMP を使ってファイルサイズをケチった画像を作るには、パレットを画像> モードから例えば256色に減色してから、PNGでエクスポートします。スクリーンショットなどはディザリングはオフにした方がごま塩のようなピクセルが入らなくて綺麗だと思います。写真などの場合はディザリングを有効にした方がいい感じになると思います。

True Color 24 bits/pixel (30,920 bytes)

Indexed Color 8 bits/pixel (10,708 bytes)


ピクセルあたりの単価が 24bit から 8bit になったので、ファイルサイズはだいたい三分の一になっているのが分かると思います。代償として減色時にパレットを使い切ってしまったときはぴったり済んだ場合を除いて情報が失われています。

上の場合は減色されて情報が失われていることが良く見ると分かります。幅広のグラデーションが階段状になっているのが見えるでしょうか?

さらに過激派な方は16色インデックスカラーに挑戦してみましょう!失われるものも多いですがファイルサイズはさらに半分になります。

Indexed Color 4 bits/pixel (5,029 bytes)