pig's diary

何でも忘れるので万年初心者ね

チャンクについて:response.on('data',fn(chunk){}) で

node.jsをやっている初心者ですが、今回はHTTPのチャンクについて自分なりに調べてみました。※チャンクについての理解が間違っていたようです。このエントリーはチャンクについては何も書かれていません。

以下にチャンクド・エンコーディングについて説明する。

HTTP/1.1サーバは、複数の要求に対応したコンテントをひとつの応答に含めて返すことができる。その場合はコンテントごとを固まり (Chunking:厚切りとか、かたまりとかいう意味だが、おだんごとか、もっと下品に馬糞とか?いうイメージ) にしてこれを複数ボディ部に収容する。このようなHTTP応答のヘッダには:

Transfer-Encoding: Chunked

なる行を含めるとともに、以下のルールでボディ部を構成する。....

http://www.cresc.co.jp/tech/java/Servlet_Tutorial/Lesson_38.htm

大きなHTMLページをリクエストするとチャンクするんでしょうか?

※チャンクしませんでした。
チャンクしました。最新node.jsの、1ページの巨大なドキュメントをリクエストしたら、約16個のチャンクが僕のさくらVPSにやって来ました。
ただ、上の引用サイトが言っている「Transfer-Encoding: Chunked」はヘッダ情報(response.headers)には入っていませんでした。なぜ・・・?
dataイベントが複数回発動したのは事実ですが、header情報に「Transfer-Encoding: Chunked」が含まれていないことからも分かるように、これはチャンクではありません。チャンクだったり、パケットだったりバッファの分断だったりいろいろ可能性があるので、dataという広い言葉を使っているのかも知れません。

チャンクについて調べる

プログラムを走らせて、チャンクを調べました。調べた事とコードは以下です。

  1. チャンクの総数
  2. チャンクのバイト数
  3. 前のチャンクとのタイムラグ
確認用のコード

このコードは、謝った理解のもと書かれています。中にある「チャンク」は、実際にはチャンクではありません。

/* query3.js

大きなHTMLをリクエストして、以下を調べます。
・チャンクは何個?
・各チャンクは何バイト?
・チャンク間はどれくらい時間が空くか
*/
Date.prototype.getDelta = function(){
    return parseInt(new Date() - this, 10);
};
// ストップウォッチのスプリットみたいな機能(前回から今の間の時間)
Date.prototype.lastTime = -1;
Date.prototype.getSplitTime = function(){
    var d = new Date();
    if (this.lastTime<0) {
        this.lastTime = d;
    }
    var s = d - this.lastTime;
    this.lastTime = d;
    return parseInt(s, 10);
};
// バイト数カウント http://www.openspc2.org/reibun/javascript/string/026/
function getByte(text) {
    count = 0;
    for (i = 0; i < text.length; i++) {
        n = escape(text.charAt(i));
        if (n.length < 4) count++;
        else count += 2;
    }
    return count;
}

var d = new Date();
var http = require('http');
var nodejsorg = http.createClient(80, 'nodejs.org');

// 1. リクエストのタイミング
console.log('request! split:' + d.getSplitTime());
var request = nodejsorg.request('GET', '/docs/v0.3.5/api/all.html',{'host': 'nodejs.org'});
request.end();
request.on('response', function (response) {
    
    // 2. レスポンスがあったタイミング
    console.log('on response! split:' + d.getSplitTime());
/*
    for (var k in response.headers){
        console.log('response.headers.' + k + ' ... ' + response.headers[k]);
    }
*/
    
    response.setEncoding('utf8');
    var chunkNum = 0;
    response.on('data', function (chunk) {
        // 3. 各チャンクのタイミング。チャンクのバイト数も見てみる
        console.log(
            ['on data! split: ', d.getSplitTime(), ' | bytes: ', getByte(chunk)].join('')
        );
        chunkNum++;
    });
    
    // 4. 全コンテンツ、ロード終了。
    response.on('end', function(){
        console.log('on end!  split: '+d.getSplitTime() + ' | sum: ' + chunkNum + ' chunks');
    });
});
結果

上記のコードを走らせた結果です。5回の結果を載せてみます。※「chunks」とありますが、実際にはchunkではありませんでした。

$ node query3.js
request! split:0
on response! split:232
on data! split: 2 | bytes: 4137
on data! split: 112 | bytes: 8688
on data! split: 114 | bytes: 17376
on data! split: 114 | bytes: 7240
on data! split: 3 | bytes: 3312
on data! split: 2 | bytes: 22746
on data! split: 109 | bytes: 15928
on data! split: 13 | bytes: 2280
on data! split: 2 | bytes: 23784
on data! split: 98 | bytes: 4344
on data! split: 3 | bytes: 12832
on data! split: 6 | bytes: 1648
on data! split: 5 | bytes: 27023
on data! split: 99 | bytes: 5792
on data! split: 1 | bytes: 1280
on end!  split: 1 | sum: 15 chunks
$ node query3.js
request! split:0
on response! split:349
on data! split: 3 | bytes: 4137
on data! split: 112 | bytes: 8688
on data! split: 113 | bytes: 2896
on data! split: 2 | bytes: 14480
on data! split: 113 | bytes: 10552
on data! split: 7 | bytes: 21298
on data! split: 32 | bytes: 2896
on data! split: 75 | bytes: 5792
on data! split: 12 | bytes: 10968
on data! split: 4 | bytes: 20888
on data! split: 23 | bytes: 2896
on data! split: 40 | bytes: 2896
on data! split: 33 | bytes: 7240
on data! split: 14 | bytes: 7040
on data! split: 1 | bytes: 22879
on data! split: 26 | bytes: 5792
on data! split: 1 | bytes: 2896
on data! split: 39 | bytes: 4176
on end!  split: 4 | sum: 18 chunks
$ node query3.js
request! split:0
on response! split:232
on data! split: 2 | bytes: 2689
on data! split: 3 | bytes: 1448
on data! split: 108 | bytes: 2896
on data! split: 2 | bytes: 2896
on data! split: 3 | bytes: 2896
on data! split: 109 | bytes: 5792
on data! split: 3 | bytes: 5792
on data! split: 3 | bytes: 5792
on data! split: 108 | bytes: 10552
on data! split: 7 | bytes: 24194
on data! split: 106 | bytes: 2896
on data! split: 8 | bytes: 13864
on data! split: 5 | bytes: 22336
on data! split: 101 | bytes: 4344
on data! split: 9 | bytes: 14280
on data! split: 6 | bytes: 22879
on data! split: 100 | bytes: 4344
on data! split: 7 | bytes: 8520
on end!  split: 4 | sum: 18 chunks
$ node query3.js
request! split:0
on response! split:395
on data! split: 1 | bytes: 4137
on data! split: 113 | bytes: 8688
on data! split: 114 | bytes: 17376
on data! split: 112 | bytes: 8688
on data! split: 4 | bytes: 1864
on data! split: 2 | bytes: 22746
on data! split: 108 | bytes: 17376
on data! split: 13 | bytes: 832
on data! split: 1 | bytes: 23784
on data! split: 99 | bytes: 4344
on data! split: 3 | bytes: 12832
on data! split: 5 | bytes: 5992
on data! split: 6 | bytes: 18824
on data! split: 100 | bytes: 10136
on data! split: 14 | bytes: 791
on end!  split: 1 | sum: 15 chunks
$ node query3.js
request! split:0
on response! split:232
on data! split: 3 | bytes: 4137
on data! split: 111 | bytes: 8688
on data! split: 115 | bytes: 17376
on data! split: 112 | bytes: 5792
on data! split: 3 | bytes: 4760
on data! split: 18 | bytes: 22746
on data! split: 93 | bytes: 8688
on data! split: 20 | bytes: 9520
on data! split: 3 | bytes: 22336
on data! split: 91 | bytes: 11584
on data! split: 20 | bytes: 7040
on data! split: 2 | bytes: 25775
on data! split: 91 | bytes: 5792
on data! split: 1 | bytes: 4176
on end!  split: 1 | sum: 14 chunks

また、大きなjpeg画像(提供:preacher_lad氏)もリクエストしてみました。

$ node query4.js
request! split:0
on response! split:283
on data! split: 1 | bytes: 2430
on data! split: 138 | bytes: 5968
on data! split: 139 | bytes: 16537
on data! split: 144 | bytes: 29116
on data! split: 3 | bytes: 4018
on data! split: 132 | bytes: 22724
on data! split: 3 | bytes: 20688
on data! split: 136 | bytes: 10556
on data! split: 6 | bytes: 43196
on data! split: 134 | bytes: 14725
on data! split: 9 | bytes: 38886
on data! split: 132 | bytes: 19048
on data! split: 12 | bytes: 49134
on data! split: 126 | bytes: 8800
on data! split: 16 | bytes: 57902
on data! split: 6 | bytes: 9650
on data! split: 117 | bytes: 24773
on data! split: 17 | bytes: 23489
on data! split: 4 | bytes: 36493
on data! split: 119 | bytes: 20601
on data! split: 28 | bytes: 906
on data! split: 2 | bytes: 57785
on data! split: 6 | bytes: 3112
on data! split: 102 | bytes: 10349
on data! split: 2 | bytes: 10320
on data! split: 29 | bytes: 34251
on data! split: 5 | bytes: 17040
on end!  split: 3 | sum: 27 chunks

思ったこと

※これらは「チャンク」ではありませんでした。

  • チャンク数は決まっていない
  • 何バイトで切るかも、決まってないように見える。
  • 100ミリ秒強ごとに、どばどばっとロードし、数chunkに分けて出しているように見えるが、定かではない。
  • あまりよくわからない。相手サーバの設定とか回線状況とかにもよる気がする。
  • 常にボディ全体にアクセスしたい場合、各chunkをconcatした総chunk文字列を、on endのタイミングで利用する、とかいうことになるのだろうか。
  • chungking express(邦題「恋する惑星」)のテーマソングがずっと流れていたが、関係ない。

はやく

何でもいいから、はやく何か作りたいです・・・