はじめまして。和田と申します。 このスレのトピックの本の訳者です。
コードを見て、クックブックのコードだな、と思ったのですが、すぐにお返事できない間にここまで伸びてしまうとは…aporo4000さんの質問内容についてはすでに皆様にお答えいただいていますので、小林さんの課題に回答してみたいと思います。 考えられる不具合として、あるリクエストが到着して(2)のプロセスに入って、複数回発生する可能性のある(3)が全て終了する前に別のリクエストの(1)が入る場合、不完全なcache[f]が存在しており、それがそのまま送信されてしまう可能性があります。 対策としては、キャッシュデータをいきなりcache[f]につめこみ始めるのではなくて、endでcacheに移す、という手段がとれるかと思います。 fs.stat(f, function(err, stats) { //(2) var bufferOffset = 0; var c = {content: new Buffer(stats.size)}; // cache[f]に直接バッファを作らない s.on('data', function(data) { //(3) data.copy(c.content, bufferOffset); bufferOffset += data.length; }); s.on('end', function(){ // end時にcache[f]に移す cache[f] = c; }); }); }).listen(8080); しかしこれでは、超巨大ファイルにアクセスが殺到するとオーバーフローしそうです。そのため、効果は限定的になるかもしれませんが、// (3)の直下にif(cache[f]) return;などの出口を設けるか、 書籍の次の節で紹介しているキャッシュファイルサイズ制限を設けて、巨大なファイルはキャッシュ対象としないことにする仕組みを併せて実装するとよいかもしれません。(書籍をお買い求めいただいてもかまいませんが、サンプルコードが http://psginc.jp/nodecookbookにあります。キャッシュファイルサイズ制限を実装したコードは1.4.1です。) 訳者として、書籍内容についてのご質問およびご指摘にお礼申し上げます。また、読者の皆様には至らぬ点お詫び申し上げます。 和田 -- yuichirow...@gmail.com (pc/mobile) mobile 090-1736-8716 @yuichirowada on twitter -- yuichirow...@gmail.com (pc/mobile) mobile 090-1736-8716 @yuichirowada on twitter 2013/4/16 Koichi Kobayashi <koic...@improvement.jp> > 小林 (koichik) です. > > 勝手に課題シリーズ第二弾w > 前のメールでも書いたように、非同期 API では > > ... //(1) > foo(hoge, function(err) { > ... //(2) > }); > ... //(3) > > の場合の処理順は (1)->(3)->(2) となります。 > > 問題のコードは HTTP を処理するリスナの中に > あるので、 > > http.createServer(function(req, res) { > ... //(1) > foo(hoge, function(err) { > ... //(2) > }); > ... //(3) > }); > > となるわけですが、ここで二つの HTTP リクエスト、 > A と B がほとんど同時に (ただし A が先に) 到着した > 場合の処理順を考えてみましょう。 > > その場合は、 > A(1)->A(3)->A(2)->B(1)->B(3)->B(2) となる。。。 > 保証はなくて、 > > A(1)->A(3)->B(1)->B(3)->A(2)->B(2) だったり > A(1)->A(3)->B(1)->B(3)->B(2)->A(2) だったり > する可能性もあります。 > リクエスト A に関する処理が全て完了してから > リクエスト B の処理が開始されるとは限らない > わけです。 > > さて、問題のコードからキャッシュの処理に着目すると、 > > http.createServer(function (request, response) { > //(1) > if(cache[f]) { > response.writeHead(200, headers); > response.end(cache[f].content); > return; > } > > fs.stat(f, function(err, stats) { > //(2) > var bufferOffset = 0; > cache[f] = {content: new Buffer(stats.size)}; > s.on('data', function(data) { > //(3) > data.copy(cache[f].content, bufferOffset); > bufferOffset += data.length; > }); > }); > }).listen(8080); > > という構造を抽出できるのですが、最初に到着した > リクエスト A の処理では、 > > (1) キャッシュが存在しないことを確認し、 > (2) キャッシュを作成して fs.stat() を呼び出し、 > (3) 'data' イベントが発生するとキャッシュの中身を設定 > > と流れていくことはもう理解できているかと思います。 > > では、この途中のどこかで別のリクエスト B が到着すると > どうなるでしょうか? > > A(1)->A(2)->A(3) > > と進む途中のどこかで、リクエスト B の処理が > 挟まってきた場合を考えてみましょう。 > > A(1)->B(1)->? > > とか、 > > A(1)->A(2)->B(1)->? > > とか、タイミングによっては B の処理が進む経路も > 変わってくるはずですね。 > > 掲載されたコードはどんな場合でも正しく > 動作するでしょうか? > うまくいかないのはどんな場合? > どんな場合でもうまく動くようにするに > どうしたらいいでしょうか? > > > On Mon, 15 Apr 2013 06:18:31 -0700 (PDT), aporo4000 <hiroshi4...@gmail.com> > wrote: > > > いまちょうどそれを勉強してる初心者なので、みなさんみたいにスラスラ書いたり出来るように頑張ります! > > > > -- > > > > --- > > このメールは Google グループのグループ「Node.js 日本ユーザグループ」の登録者に送られています。 > > このグループから退会し、メールの受信を停止するには、nodejs_jp+unsubscr...@googlegroups.comにメールを送信します。 > > その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。 > > > > > -- > { > name: "Koichi Kobayashi", > mail: "koic...@improvement.jp", > blog: "http://d.hatena.ne.jp/koichik/", > twitter: "@koichik" > } > > -- > > --- > このメールは Google グループのグループ「Node.js 日本ユーザグループ」の登録者に送られています。 > このグループから退会し、メールの受信を停止するには、nodejs_jp+unsubscr...@googlegroups.comにメールを送信します。 > その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。 > > > -- --- このメールは Google グループのグループ「Node.js 日本ユーザグループ」の登録者に送られています。 このグループから退会し、メールの受信を停止するには、nodejs_jp+unsubscr...@googlegroups.com にメールを送信します。 その他のオプションについては、https://groups.google.com/groups/opt_out にアクセスしてください。