Hatena::Groupnodejs

Node.jsで遊ぶよ

2011-03-10

そんな V8 で大丈夫か?

07:51

という話がありました。

書いてるのは Yahoo の Bruno Fernandez-Ruiz さんで、V8サーバーサイドでの実績や Googleサーバーサイド JS に対する意識がわからないので不安だという話。また、イベントループのサーバースレッドプロセスモデルに比べて落ちたときの影響がでかいだろと。


それに対して Joyent の創業者の一人 Jason Hoffman さんの返事がおもしろかったです。

もし安定性に問題があるなら例を示せ。それができないなら何を心配してるんだと。

もし Node.js のために V8 をフォークしなければいけないことになれば、Joyent が責任をもって本家を追従すると。Joyent は過去にも、以下のような本家に追従するフォークを作ってきたし、そういうエンジニア的チャレンジならお任せよ!と。

Some examples: We “branched” the Solaris 11 Kernel in 2005 and tracked all Nevada put backs for 5 years (this did eventually lead to a full fork); we’ve been a NetBSD pkg-src user and contributor for 4 years; we worked with Apple on the version of Ruby (with DTrace probes) that shipped back with Leopard.

If we end up in a situation like OpenSolaris’s dissolution, then we’ll do what we did then: We’ll fork it. And fork it hard. Fork it like it’s never been forked before.


また、前 Yahoo で今 Facebook に移った Peter Greiss さんも反応してます。

こちらは幾分冷静で、イベントベースサーバースレッドベースのサーバーより安定性に問題があるかどうかは一概には言えないとか書いてます。むしろデッドロックが無いぶん良くね?とか。

ところで Peter さんは Node.js のヨサゲなライブラリをいっぱい書いてたんですが、Facebook に行ってからメンテされてなくてちょっと残念です。NodeWorker とか。


そんなところ。

jvctdadmaujvctdadmau2014/01/19 15:52rzhliopefkt, <a href="http://www.ilhvwdmaps.com/">ngyibhkerc</a>

qrhcmutuqhqrhcmutuqh2014/01/22 05:17ydvjgopefkt, <a href="http://www.oxolpwxtmy.com/">xccdfnqogy</a>

lyppgstdhelyppgstdhe2014/01/25 09:09nghdaopefkt, http://www.wajblnhllj.com/ tnmiwskgci

cwnkbvjzqhcwnkbvjzqh2014/01/30 10:52rhcfjopefkt, <a href="http://www.arevnwdsbg.com/">kpgancqwzm</a> , [url=http://www.rnntjqlqyp.com/]irtscjmzmt[/url], http://www.cvwrqcviax.com/ kpgancqwzm

dhsrsyebhodhsrsyebho2014/06/03 09:13klncmopefkt, <a href="http://www.bstnkftdsn.com/">kullmsxmeu</a> , [url=http://www.jrrhffnjxo.com/]enxqqdhmgp[/url], http://www.qwwpxsauht.com/ kullmsxmeu

2010-12-14

C++ で node.js ライブラリを作る・その2

09:44

前回の続き。

コールバックベースの非同期関数を作る。

具体的には、前回こんな感じだった write を EIO スレッドのほうでやることにする。

    int Write (const char *str) {
      return fprintf(fp, "%s\n", str);
    }

    static Handle<Value>
    Write (const Arguments& args) {
      HandleScope scope;
      Unwrap<File>(args.This())->Write(*String::Utf8Value(args[0]));
      return Undefined();
    }

こうなる。C++ なのに C 言語風なのはご愛敬。struct じゃなくて class にしてもよかったんだけど、行数が増えるので今回は割愛。

    int Write (const char *str) {
      return fprintf(fp, "%s\n", str);
    }

    struct WriteData {
      File *file;
      char *str;
      Persistent<Value> cb;
    };

    static int
    AfterWrite(eio_req *req) {
      HandleScope scope;
      ev_unref(EV_DEFAULT_UC);

      WriteData *write_data = (WriteData*)(req->data);

      if (write_data->cb->IsFunction()) {
        Local<Value> argv[1];
        argv[0] = req->result < 0 ?
          Local<Value>::New(String::New("Error")) :
          Local<Value>::New(Undefined());
        Persistent<Function>::Cast(write_data->cb)->Call(
            write_data->file->handle_, 1, argv);
      }

      write_data->file->Unref();
      write_data->cb.Dispose();
      free(write_data->str);
      free(write_data);
      return 0;
    }

    static int
    EIO_Write(eio_req *req) {
      WriteData *write_data = (WriteData*)(req->data);
      req->result = write_data->file->Write(write_data->str);
      return 0;
    }

    static Handle<Value>
    Write (const Arguments& args) {
      HandleScope scope;

      WriteData *write_data = (WriteData *)malloc(sizeof(WriteData));
      write_data->file = Unwrap<File>(args.This());
      write_data->str = strdup(*String::Utf8Value(args[0]));
      write_data->cb = Persistent<Value>::New(args[1]);

      eio_custom(EIO_Write, EIO_PRI_DEFAULT, AfterWrite, write_data);

      write_data->file->Ref();
      ev_ref(EV_DEFAULT_UC);
      return Undefined();
    }

何やってるのか一発で分かる人は少ないと思うので説明。下のほうから。

      eio_custom(EIO_Write, EIO_PRI_DEFAULT, AfterWrite, write_data);

これが任意の操作を EIO スレッドのほうで実行するための命令。EIO スレッドの話は↓に書いた。

EIO_Write は EIO スレッドのほうで実行される関数AfterWriteEIO_Write が終わった後でメインスレッドで実行される関数。どちらも eio_req *req というポインターが渡される。eio_req には data という任意のポインターを入れられるフィールドが開けてあるので、そこに write_data を渡してやる。

write_data はこんなふうに作った。

      WriteData *write_data = (WriteData *)malloc(sizeof(WriteData));
      write_data->file = Unwrap<File>(args.This());
      write_data->str = strdup(*String::Utf8Value(args[0]));
      write_data->cb = Persistent<Value>::New(args[1]);
  • file は、今書いている File オブジェクト
  • str はファイルに書き出す文字。
    • String::Utf8ValueHandleScope の消滅とともに GC されてしまうので、strdup で新しく malloc してある。
  • cb はコールバック。
    • Persistent Handle を作っているので、非同期操作を実行されている間どこからも参照されていない状態になるけど GC されない。
    • Persistent と Local の違いはここに書いた。

そして、write 関数自体は undefined を返す。

      write_data->file->Ref();
      ev_ref(EV_DEFAULT_UC);
      return Undefined();
  • node::ObjectWrap::Ref をやることによって、JSオブジェクトGC されなくする。
  • ev_ref(EV_DEFAULT_UC); は EIO スレッドの参照カウントを増やして node.js のイベントループを抜けてしまわないようにする。
    • 参照されているスレッドの数がゼロになると node.js が終了する、と node.cc のコメントに書いてあった。

次に EIO スレッドで実行される部分を見る。

    int Write (const char *str) {
      return fprintf(fp, "%s\n", str);
    }

    static int
    EIO_Write(eio_req *req) {
      WriteData *write_data = (WriteData*)(req->data);
      req->result = write_data->file->Write(write_data->str);
      return 0;
    }

やってることは見たら分かると思う。

V8スレッドセーフではないので(スレッドセーフにするには各スレッドでグローバルコンテクスト初期化しないといけないので)、ここでは V8オブジェクトを触ってはいけない。

req->result の型は ssize_t で、普通はエラーコードなどを入れる。fprintf の返り値は、「成功すれば書きこまれたバイト数、失敗すればマイナス」となるらしい。(すっかり失念してた…)

最後にメインスレッドに戻って、AfterWrite を実行。

    static int
    AfterWrite(eio_req *req) {
      HandleScope scope;
      ev_unref(EV_DEFAULT_UC);

      WriteData *write_data = (WriteData*)(req->data);

      if (write_data->cb->IsFunction()) {
        Local<Value> argv[1];

        argv[0] = req->result < 0 ?
          Local<Value>::New(String::New("Error")) :
          Local<Value>::New(Undefined());

        Persistent<Function>::Cast(write_data->cb)->Call(
            write_data->file->handle_, 1, argv);
      }

      write_data->file->Unref();
      write_data->cb.Dispose();
      free(write_data->str);
      free(write_data);
      return 0;
    }
  • ev_unref(EV_DEFAULT_UC); で EIO スレッドの参照カウントを減らす。
  • コールバックが渡されていればそれを実行。
    • v8::Function::Call には JS の this にあたるオブジェクトを与えるのだが、それは node::ObjectWrap::handle_ で元々の this だったものが取れるので、それを渡してる。
    • node.js の決まりで、コールバックに渡す最初の引数はエラーということになっている。(エラーがなければ第一引数を undefined にしたりもありうると)
  • 最後に色んな物を GC する。
    • write_data->file->Unref()JSオブジェクトGC してもいいよという意味。
    • write_data->cb->Dispose() は Persistent Handler を GC に回してもいいよという意味。

さて、気になるベンチマークなんだけど、非同期にするとかなり遅くなる。

同期。(test.js)

var File = require('./build/default/file').File;
var file = new File('bar.txt');

var N = 10000;
for (var i = 0; i < N; i++) {
  file.writeSync(i);
}

console.log('done');

非同期。(async.js)

var File = require('./build/default/file').File;
var file = new File('foo.txt');

var N = 10000;
var n = 0;

for (var i = 0; i < N; i++) (function test(i) {
  file.write(i, function(err) {
    if (err) {
      console.log([i, err]);
    }
    if (++n === N) {
      console.log('done');
    }
  });
}(i));

結果。

% time node test.js
done
node test.js  0.02s user 0.06s system 89% cpu 0.094 total
% time node async.js
done
node async.js  0.07s user 2.62s system 99% cpu 2.710 total

30倍近く…

これは、EIO スレッドとの通信にかかるオーバーヘッドが非常に大きいことに加え、非同期にしてるのに結局一つのファイルを扱っていることなどが考えられる。色んなジョブを並列で扱うともっと変わってくるのかもしれない。


気が向いたら「その3」も書く。

その3は EventEmitter の話にしようかと思ったけど既にあった→node.jsのモジュール作り方メモ - 三次元日誌

zbuvfloiklzbuvfloikl2013/12/17 20:42vmnytopefkt, <a href="http://www.qnfuspsblg.com/">zjqggvjhso</a> , [url=http://www.nmcivaxpkg.com/]bzenphneyq[/url], http://www.pppqlojipk.com/ zjqggvjhso

tftebsbvoatftebsbvoa2014/12/03 10:33mdqqaopefkt, <a href="http://www.rylimascxd.com/">emsazfybwd</a> , [url=http://www.hzcicxywnx.com/]fajilazsrb[/url], http://www.siuuihrnpd.com/ emsazfybwd

SailiphieseSailiphiese2017/05/04 02:22http://undeclaiming.xyz <a href="http://undeclaiming.xyz">norsk kasino</a> http://undeclaiming.xyz - norsk kasino

2010-12-12

C++ で node.js ライブラリを作る・その1

03:19

非同期の説明の前にまず同期ライブラリを書いてみる。

node::ObjectWrap というのが、JavaScriptオブジェクトの内部に任意の C のオブジェクトポインター)を持たせるための仕組みで、それを継承すると簡単。

  • file.cc
#include <node.h>
#include <stdio.h>

using namespace v8;
using namespace node;

class File : ObjectWrap {
  public:
    static void
    Initialize (const Handle<Object> target) {
      HandleScope scope;
      Local<FunctionTemplate> tmpl = FunctionTemplate::New(New);
      tmpl->InstanceTemplate()->SetInternalFieldCount(1);
      NODE_SET_PROTOTYPE_METHOD(tmpl, "write", Write);

      target->Set(String::New("File"), tmpl->GetFunction());
    }

    ~File () {
      fclose(fp);
    }
  private:
    FILE *fp;

    File (const char *filename) {
      if ((fp = fopen(filename, "w")) == NULL) {
        throw "file_open_error";
      }
    }

    static Handle<Value>
    New (const Arguments& args) {
      HandleScope scope;
      if (!args.IsConstructCall()) return args.Callee()->NewInstance();
      try {
        (new File(*String::Utf8Value(args[0])))->Wrap(args.This());
      } catch (const char *msg) {
        return ThrowException(Exception::Error(String::New(msg)));
      }
      return args.This();
    }

    int Write (const char *str) {
      return fprintf(fp, "%s\n", str);
    }

    static Handle<Value>
    Write (const Arguments& args) {
      HandleScope scope;
      Unwrap<File>(args.This())->Write(*String::Utf8Value(args[0]));
      return Undefined();
    }
};

extern "C" void
init (Handle<Object> target) {
  File::Initialize(target);
}

それと同じディレクトリに wscript というファイルを作って、こんなふうに書いて、

srcdir = "."
blddir = "build"
VERSION = "0.0.1"

def set_options(opt):
  opt.tool_options("compiler_cxx")

def configure(conf):
  conf.check_tool("compiler_cxx")
  conf.check_tool("node_addon")

def build(bld):
  obj = bld.new_task_gen("cxx", "shlib", "node_addon")
  obj.source = "file.cc"
  obj.target = "file"

node-waf configure && node-waf すると、build というディレクトリができて、その中に build/default/file.node というファイルができる。

node.js からはこんなふうに使える。

var File = require('./build/default/file').File;
var file = new File("foo.txt");
file.write("foo");

ポイントは、

など。

2010-12-04

node-msgpack

05:25

Griess さんはどうやらもうメンテしてなくて、npm で入るやつは tomtaylor さんのやつなんだけど、それでも古い。

いつからか知らないが今は Buffer::lengthBuffer::data プロパティが private になってて、Buffer::Length(Handle<Object>)Buffer::Data(Handle<Object>) という static 関数を使うことになってるのでそれを反映しないといけない。

それにしても遅い。

うちでは bench.js がこんな感じ。

msgpack pack:   8457 ms
msgpack unpack: 1817 ms
json    pack:   3863 ms
json    unpack: 1856 ms

5月に Griess さんが書いてるところでは

msgpack pack:   5.8 秒
msgpack unpack: 8.62 秒
json    pack:   7.17 秒
json    unpack: 22.18 秒

ぐらいだったらしい。

何が起こったんだろ。


ちなみに uupaa さんのやつだと、toString スイッチオンでこのぐらい。

msgpack pack:   2405 ms
msgpack unpack: 3371 ms
json    pack:   3683 ms
json    unpack: 1800 ms

toString しなかったらこんなに速い。

msgpack pack:   2082 ms
msgpack unpack: 2134 ms
json    pack:   4194 ms
json    unpack: 2108 ms

2010-10-20

リクエスト失敗時に Node.js が終了してしまう

12:24

http = require('http');

var cli = http.createClient(80, '0.0.0.0');
var req = cli.request('GET', '/');
req.end();
req.on('response', function(res) {
  console.log(res);

  res.on('data', function() {
    //
  });
  res.on('end', function() {
    console.log('finish!');
  });
});

こんなことをよくやると思うが、もしサーバーが存在しなかったり、通信中にエラーがあったりすると、node.js が止まってしまうことがよくある。上の 0.0.0.0 は極端な例だけど、普通のリクエストの途中で終わったりすることもよくある。

node.js:50
    throw e;
    ^
Error: ECONNREFUSED, Connection refused
    at IOWatcher.callback (net:870:22)
    at node.js:607:9

これが起こるのは、httpClient の error イベントを catch していないため。

cli.on('error', function(e) {
  console.log(e);
});

これで解決。on('error') というのは Node.js では非同期 catch 相当になっている。

Event: 'error'

function (exception) { }

If an error was encountered, then this event is emitted. This event is special - when there are no listeners to receive the error Node will terminate execution and display the exception's stack trace.
http://nodejs.org/api.html

でもこれだと、連続してリクエスト送ってて一つがエラー出したときとかに、どれがエラー出したかを知ることができない。cli.on('error') じゃなくて req.on('error') のほうがいいのになあ…

PhiliphiesePhiliphiese2017/04/12 04:28http://stemmeries.xyz <a href="http://stemmeries.xyz">norsk kasino</a> http://stemmeries.xyz - norsk kasino

SailiphieseSailiphiese2017/05/25 00:33http://undeclaiming.xyz <a href="http://undeclaiming.xyz">norsk kasino</a> http://undeclaiming.xyz - norsk kasino

DannybigDannybig2017/06/27 00:12Attention Required! | Cloudflare
<a href=http://acheterdufrance.com/>Attention Required! !</a>

TimothychegeTimothychege2017/07/04 12:09301 Moved Permanently
<a href=https://www.viagrapascherfr.com/>Click here>>></a>