小林 (koichik) です.

> ただ isaacs が書いているよう http.ServerRequest 等の場合は、response イ
> ベントが既に fd からデータバッファを読み込んでいる後なので pause() でも
> きっちり止まらず、data が漏れ出てきちゃう可能性があります。 なので注意が
> 必要です。

http の場合、v0.6 までは止まるとは限りませんが、
v0.8 の場合はきっちり確実に止まります。
IncomingMessage 自体がバッファリングして、pause()
中は 'data' イベントを emit() しないからです
(fs.ReadStream も同様)。

Streams1 の問題点の一つは、上記のように各ストリームが
個別にバッファリングしていたりしていなかったり、
バラバラに対応していたことでしょうね。

とはいえ、v0.8 ではバッファリングしない (pause で
止まるとは限らない) ストリームは結局 net.Socket だけに
なってた気がしますけどw

> fs.stat のエラーをひっかけて 404 の処理をする方が効率的だと思います。 

あう、そこを指摘すると課題 1 が成立しなくなって
あうあうあー


On Fri, 19 Apr 2013 10:34:01 +0900, Shigeki Ohtsu <oh...@iij.ad.jp> wrote:

> 大津です。
> 
> # 初心者スレなのに続けてしまってすみまへん
> 
> こいうい問題があって stream2 へと続いていくんですが、これに関しては東京
> Node学園祭の isaacs の講演資料
> 
> https://dl.dropboxusercontent.com/u/3685/presentations/streams2/streams2-nodefest.pdf
> 
> の p26 「immediate 'data'」のスライドを参照されるとよいかと思います。
> 
> 続く p27 に 「pause() でもダメだ!」 と書いてありますが、
> fs.fs.ReadStream の場合は nextTick で read(2) するのできっちり止まりま
> す。(さすが koichik さん!)
> 
> ただ isaacs が書いているよう http.ServerRequest 等の場合は、response イ
> ベントが既に fd からデータバッファを読み込んでいる後なので pause() でも
> きっちり止まらず、data が漏れ出てきちゃう可能性があります。 なので注意が
> 必要です。
> 
> で、先の返事でもあったように promise っぽくキャッシュ生成後にレスポンス
> を返すと巨大ファイルだとキャッシュ生成時間分待たされるので、できれば
> キャッシュ生成しながら、追っかけ再生みたいにキャッシュバッファを吐き出せ
> ればいいんですが、チト簡単に思いつきませんでした。
> でも、これで来月のハッカソンのお題が見つかったのでラッキーです。
> 
> あと一つサンプルコードに注文を付けさせていただくと、最初 fs.exists() で
> ファイルの存在をチェックしていますが、これ中身は fs.stat() なので結局2
> 回 stat(2) を行うことになり無駄です。
> fs.stat のエラーをひっかけて 404 の処理をする方が効率的だと思います。 こ
> こで Domains の登場ですが、どう使うかは「Node.js入門」をご覧くださいw
> (ステマ)
> 
> (2013/04/19 8:54), yuichiro wada wrote:
> > 小林さん
> > 
> > ありがとうございます。
> > 恥ずかしながら見えていませんでした。
> > # 大津さんが仰っていたのもこれだったのですね。
> > 
> > 取り急ぎお礼まで。
> > 
> > 
> > --
> > yuichirow...@gmail.com <mailto:yuichirow...@gmail.com> (pc/mobile)
> > mobile 090-1736-8716
> > @yuichirowada on twitter
> > 
> > 
> > 
> > 2013/4/19 Koichi Kobayashi <koic...@improvement.jp 
> > <mailto:koic...@improvement.jp>>
> > 
> >     小林 (koichik) です.
> > 
> >     ともあれ (JW)、一つ目の課題について自分が
> >     想定していた問題点を書いてみます。
> > 
> >     サンプルのコードは以下の構造を持っています。
> > 
> >     var s = fs.createReadStream(f).once('open', function () {
> >        this.pipe(response);
> >     });
> >     fs.stat(f, function(err, stats) {
> >        s.on('data', function(data) {
> >        });
> >     });
> > 
> >     pipe() が使われていますが、その中では 'data'
> >     (v0.10 以降では 'readable') イベントのリスナが
> >     登録されるので、それを表に出すと以下のように
> >     なります。
> > 
> >     var s = fs.createReadStream(f).once('open', function () {
> >        //(1)
> >        s.on('data', function(data) {
> >          //(2)
> >        });
> >     });
> >     fs.stat(f, function(err, stats) {
> >        //(3)
> >        s.on('data', function(data) {
> >          //(4)
> >        });
> >     });
> > 
> >     ここで、(1)->(2) および (3)->(4) はこの順序で
> >     実行されますが、それぞれの間では順序の保証は
> >     ありません。
> > 
> >     そのため、もし fs.stat() に時間がかかると、
> >     (3) のところで 'data' リスナを登録するよりも
> >     先に、最初の 'data' イベントが発生してしまう
> >     可能性があります。
> > 
> >     その場合、始めの方のデータは (2) の 'data'
> >     リスナだけが受信し、(4) の 'data' リスナは
> >     それを取りこぼすことになってしまいます。
> > 
> >     これは現実的にはまず起きないとは思いますが、
> >     似た状況は setTimeout() を作ることができます。
> > 
> >     var s = fs.createReadStream(f).once('open', function () {
> >        this.pipe(response);
> >     });
> >     setTimeout(function() {
> >        fs.stat(f, function(err, stats) {
> >          s.on('data', function(data) {
> >          });
> >        });
> >     }, 1000); //時間は適当
> > 
> > 
> >     これが一つ目の課題として想定した問題点です。
> > 
> > 
> >     回避策としては、pause()/resume() を使うのが
> >     素直なやり方かと思います。
> > 
> >     var s = fs.createReadStream(f).once('open', function () {
> >        this.pipe(response);
> >     });
> >     s.pause(); //一時停止
> >     fs.stat(f, function(err, stats) {
> >        s.resume(); //再開
> >        s.on('data', function(data) {
> >        });
> >     });
> > 
> >     v0.8 以前の場合、pause() は「アドバイス的」な
> >     ものということになっていますが、fs.ReadStream は
> >     かなり初期から (たぶん v0.2 の頃から) 中断中は
> >     'data' イベントを生成せずにバッファリングしてくれて
> >     いたのでこれで大丈夫。
> > 
> >     v0.10 以降で導入された Stream2 では pause() は
> >     「アドバイス的」ではなくなったのでやっぱり
> >     大丈夫、そして pause() を呼び出すと "oldMode" に
> >     切り替わるので、後から 'data' リスナを追加しても
> >     安心になります。
> > 
> > 
> > 
> >     On Wed, 17 Apr 2013 02:40:00 +0900, Koichi Kobayashi
> >     <koic...@improvement.jp <mailto:koic...@improvement.jp>> wrote:
> > 
> >      > 小林 (koichik) です.
> >      >
> >      > なぜ初心者には見えない人達が盛り上がってるのか
> >      > 小一時間(ry
> >      >
> >      > ともあれ (JW)、翻訳書からのサンプルなので、
> >      > 時期的に原書が v0.10 に対応しているはずがなく、
> >      > ここでは Stream1 の話ということにした方が
> >      > いいんじゃないかと思いますけれど。
> >      >
> >      > > てことで、「fs.stat() も open イベント内部に押し込むと、意図し
> >     た順序で処理してくれる。」てのが課題に対する私の解答。
> >      >
> >      > んー、自分が想定 (期待) したのと違う問題を
> >      > 解決しようとしてるようなw
> >      >
> >      > fs.stat() を 'open' イベントリスナの中に押し込むと、
> >      > fs.stat() のコールバックが呼び出されるタイミングは
> >      > 元のコードよりさらに遅くなりますよね。
> >      >
> >      > すると、キャッシュのバッファを埋めるための 'data'
> >      > イベントのリスナを登録するのもさらに遅くなりますね。
> >      > それで大丈夫でしょうか?
> >      >
> >      > たとえば極端な例として、
> >      >
> >      > var s = fs.createReadStream(f).once('open', function() {
> >      >   response.writeHead(200, headers);
> >      >   this.pipe(response);
> >      > });
> >      >
> >      > setTimeout(function() {
> >      >   s.on('data', function(buf) {...});
> >      > }, 10 * 1000); // 10 秒!!!!
> >      >
> >      > だったらどうでしょうか?
> >      > setTimeout() の中で登録した 'data' リスナは
> >      > 適切に呼び出されるでしょうか?
> >      >
> >      >
> >      > On Tue, 16 Apr 2013 08:11:48 +0900, Akitoshi Manabe
> >     <akitoshi.man...@gmail.com <mailto:akitoshi.man...@gmail.com>> wrote:
> >      >
> >      > > 眞鍋です。
> >      > >
> >      > > スミマセン。誤解してました。
> >      > > 以下のファイルを書いて試したところ、fs.createReadStream だと
> >      > > readable イベントは発火しませんでした。
> >      > >
> >      > > ---- (ここから)
> >      > > var util = require('util');
> >      > > var fs = require('fs');
> >      > > var f = __filename;
> >      > >
> >      > > var s = fs.createReadStream(f, {bufferSize: 128 }); // default
> >     : 64 * 1024
> >      > > [byte]
> >      > > s.on( 'readable', function(){ util.puts( 'readable', arguments
> >     ) } );//発火せず
> >      > > s.on( 'open', function(){
> >      > >     util.puts( 'open', arguments )
> >      > >     // data イベントはopen後でも追加できる。
> >      > >     s.on('data', function( d ){ util.puts('DATA ', d) })
> >      > > } );
> >      > > s.on( 'error', function(){ util.puts( 'error', arguments ) } );
> >      > > s.on( 'close', function(){ util.puts( 'close', arguments ) } );
> >      > > ---- (ここまで)
> >      > >
> >      > > 実は、readable イベントに押し込めるのではないかと考えていましたが、
> >      > > 「data イベントは open 直後に実行されるハンドラ内に定義しても良
> >     い」と確認できます。
> >      > > てことで、「fs.stat() も open イベント内部に押し込むと、意図し
> >     た順序で処理してくれる。」てのが課題に対する私の解答。
> >      > >
> >      > >
> >      > > Nodeを始めた頃「関数のネスト(ピラミッドとも)で書く特徴」に悩
> >     まされました。
> >      > > 理解が深まってくると、ピラミッドをフラットで見やすく書く為の
> >      > > 「フロー制御」を考えたくなると思います。
> >      > >
> >      > >
> >      > >
> >      > >
> >      > > 2013年4月16日 7:08 aporo4000 <hiroshi4...@gmail.com
> >     <mailto:hiroshi4...@gmail.com>>:
> >      > >
> >      > > > 全然違うかもしれませんが、最初に読み込むバイト数を指定すると
> >     か!?ですか?
> >      > > >
> >      > > > 2013年4月16日火曜日 1時20分00秒 UTC+9 koichik:
> >      > > >>
> >      > > >> 小林 (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
> >     <hiros...@gmail.com <mailto:hiros...@gmail.com>>
> >      > > >> wrote:
> >      > > >>
> >      > > >> > いまちょうどそれを勉強してる初心者なので、みなさんみたいに
> >     スラスラ書いたり出来るように頑張ります!
> >      > > >> >
> >      > > >> > --
> >      > > >> >
> >      > > >> > ---
> >      > > >> > このメールは Google グループのグループ「Node.js 日本ユーザ
> >     グループ」の登録者に送られています。
> >      > > >> > このグループから退会し、メールの受信を停止するには、
> >     nodejs_jp+unsubscr...@googlegroups.com
> >     <mailto:nodejs_jp%2bunsubscr...@googlegroups.com>にメールを送信します。
> >      > > >> > その他のオプションについては、https://groups.google.com
> >     /groups/opt_out にアクセスしてください。
> >      > > >> >
> >      > > >>
> >      > > >>
> >      > > >> --
> >      > > >> {
> >      > > >>   name: "Koichi Kobayashi",
> >      > > >>   mail: "koi...@improvement.jp <mailto:koi...@improvement.jp>",
> >      > > >>   blog: "http://d.hatena.ne.jp/koichik/";,
> >      > > >>   twitter: "@koichik"
> >      > > >> }
> >      > > >>
> >      > > >>  --
> >      > > >
> >      > > > ---
> >      > > > このメールは Google グループのグループ「Node.js 日本ユーザグ
> >     ループ」の登録者に送られています。
> >      > > > このグループから退会し、メールの受信を停止するには、
> >     nodejs_jp+unsubscr...@googlegroups.com
> >     <mailto:nodejs_jp%2bunsubscr...@googlegroups.com>にメールを送信します。
> >      > > > その他のオプションについては、https://groups.google.com
> >     /groups/opt_out にアクセスしてください。
> >      > > >
> >      > > >
> >      > > >
> >      > >
> >      > > --
> >      > >
> >      > > ---
> >      > > このメールは Google グループのグループ「Node.js 日本ユーザグ
> >     ループ」の登録者に送られています。
> >      > > このグループから退会し、メールの受信を停止するには、
> >     nodejs_jp+unsubscr...@googlegroups.com
> >     <mailto:nodejs_jp%2bunsubscr...@googlegroups.com> にメールを送信します。
> >      > > その他のオプションについては、https://groups.google.com/groups
> >     /opt_out にアクセスしてください。
> >      > >
> >      > >
> >      >
> >      >
> >      > --
> >      > {
> >      >   name: "Koichi Kobayashi",
> >      >   mail: "koic...@improvement.jp <mailto:koic...@improvement.jp>",
> >      >   blog: "http://d.hatena.ne.jp/koichik/";,
> >      >   twitter: "@koichik"
> >      > }
> >      >
> >      > --
> >      >
> >      > ---
> >      > このメールは Google グループのグループ「Node.js 日本ユーザグルー
> >     プ」の登録者に送られています。
> >      > このグループから退会し、メールの受信を停止するには、
> >     nodejs_jp+unsubscr...@googlegroups.com
> >     <mailto:nodejs_jp%2bunsubscr...@googlegroups.com> にメールを送信します。
> >      > その他のオプションについては、https://groups.google.com/groups
> >     /opt_out にアクセスしてください。
> >      >
> > 
> > 
> >     --
> >     {
> >        name: "Koichi Kobayashi",
> >        mail: "koic...@improvement.jp <mailto:koic...@improvement.jp>",
> >        blog: "http://d.hatena.ne.jp/koichik/";,
> >        twitter: "@koichik"
> >     }
> > 
> >     --
> > 
> >     ---
> >     このメールは Google グループのグループ「Node.js 日本ユーザグループ」
> >     の登録者に送られています。
> >     このグループから退会し、メールの受信を停止するには、
> >     nodejs_jp+unsubscr...@googlegroups.com
> >     <mailto:nodejs_jp%2bunsubscr...@googlegroups.com> にメールを送信します。
> >     その他のオプションについては、https://groups.google.com/groups
> >     /opt_out にアクセスしてください。
> > 
> > 
> > 
> > -- 
> > 
> > ---
> > このメールは 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 にアクセスしてください。
> 


-- 
{
  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 にアクセスしてください。


メールによる返信