SlideShare a Scribd company logo
1 of 50
Download to read offline
Node.js 入門
                       2011 年 4 月 16 日


                             森 俊夫 @ 徳島
                     forest1040@gmail.com
            http://d.hatena.ne.jp/forest1040/




                                         1
自己紹介
   id:forest1040  です。
   徳島で、フリーランスをやってます。
   Web 系エンジニアです。最近は、 Java EE(JBoss 
     Seam) と Ruby をよく使っています。
   1年前に息子が生まれ、イクメン中です。
   Node.js on Android をやってます。




                                       2
アジェンダ
   基礎編
            Node.js とは
            非同期 I/O とイベントループ
            Node.js のアーキテクチャ
   実践編
            インストール
            デバッグ環境
            Node.js を使ったリアルタイム通信


                                    3
Node.js とは
   サーバサイド JavaScript
   Google の V8 エンジン搭載
   シングルスレッド非同期 I/O 環境
   イベントループモデル




                           4
シングルスレッド非同期 I/O 環境
           と
        イベントループ




                         5
頭の体操(並行処理)

    突然ですが、クイズです。
   シングルスレッドで並行処理を行うには?
   マルチスレッドとシングルスレッドの
    並行処理の違いは?




                          6
並行処理
   マルチスレッド( multithread )による並行処理
    呼び元と並行に処理が行われる。
   シングルスレッドでのコールバック( callback )によ
     る並行処理
    呼び元がプロセッサを使用していないときに処理
     が行われる。




                                     7
非同期と同期、ノンブロッキングとブロッキング

   さらにクイズです。
    非同期とノンブロックの違いがわかります
     か?




                          8
非同期とノンブロッキング
   実は、非同期( Asynchronous )とノンブロッキング
     ( non­blocking )は同じ意味としてよく使われて
     いる。
   日本語に惑わされると負け。




                                      9
非同期(ノンブロッキング)とは
    非同期呼び出し( Asynchronous Call )と同期呼び
     出し( Synchronous Call )の違い
   同期呼び出し( Synchronous Call )
    通常メソッドを呼び出すとメソッド内の処理が完了
     するまで、呼び出し元には戻ってこない。このよう
     なメソッド呼び出しのこと。
   非同期呼び出し( Asynchronous Call )
    メソッドを呼び出した瞬間に呼び出し元に処理が
     戻ってくるような呼び出しのこと。非同期で呼び
     出されたメソッドは、環境によって処理されるタイ
     ミングが変わる。   まさしくノンブロッキング
                                    10
ノンブロッキング I/O とは
    ブロッキング I/O ( blocking I/O )とノンブロッキン
     グ I/O ( non­blocking I/O )の違い
   ブロッキング I/O ( blocking I/O )
    データ処理が完了するまで待たされること。
   ノンブロッキング I/O ( non­blocking I/O )
    データ処理の完了を待たされずに、他の処理を行
     えること。



                                          11
I/O モデル
     W. Richard Stevens の
     「 UNIX ネットワークプログラミング 第 2 版 Vol.1 」
   Synchronous I/O Operation ( 同期 I/O 操作 )
              blocking I/O ( ブロッキング I/O)
              non­blocking I/O ( 非ブロッキング I/O)
              I/O multiplexing (I/O 多重化 )
              signal driven I/O ( シグナル駆動 I/O)
   Asynchronous I/O Operation ( 非同期 I/O 操作 )
              Asynchronous I/O ( 非同期 I/O)

                                                 12
Boost application performance using 
           asynchronous I/O




引用: Boost application performance using asynchronous I/O
http://www­128.ibm.com/developerworks/linux/library/l­async/
                                                               13
Boost application performance using 
           asynchronous I/O



 この図では、 I/O multiplexing が
Asynchronous に分類されている。




引用: Boost application performance using asynchronous I/O
http://www­128.ibm.com/developerworks/linux/library/l­async/
                                                               14
C10K 問題
   ハードウェアの性能上は問題がなくても、あまりにもクライアントの数
     が多くなるとサーバがパンクする問題のこと。
    ◇ 解決方法
1. 各スレッドが複数のクライアントを受け付ける。 そしてノンブロッキン
    グ I/O  と レベル・トリガ型の完了通知 (level­triggered readiness 
    notification) を利用する。
2. 各スレッドが複数のクライアントを受け付ける。 そしてノンブロッキン
    グ I/O  と 変更型の完了通知 (readiness change notification) を利
    用する。
3. 各スレッドが複数のクライアントを受けつける。 そして非同期 I/O  を
    使う。
4. 各スレッドが一つのクライアントを受けつける。 そしてブロッキング
    I/O  を使う。
5. サーバのコードをカーネルに組込む。

                                                      15
マルチスレッド vs  ノンブロッキング
   apache  マルチスレッドモデル (C10K の解決案 No.4)
   nginx  ノンブロッキング( C10K の解決案 No.1 )
    1秒あたりの処理リクエスト数                                 メモリ使用量




    nginx  は、最高で秒間 10,000 リクエストを処理する。同時接続数を
      増やしても、リクエスト処理数は少々減る程度
   apache の場合、同時接続数を増やすと著しくリクエスト処理数が減
      る。しかも、メモリ使用量が同時接続数に比例して増える。
    つまり、同時接続数が多い場合は、ノンブロッキングが有利!!
参照: http://blog.webfaction.com/a­little­holiday­present
                                                            16
Node.js の場合
   「 3.  各スレッドが複数のクライアントを受けつける。 そして
      非同期 I/O  を使う」を採用
   「 libev 」と「 libeio 」を使用して、非同期 I/O 環境を実装
   libev
    C 言語で書かれたイベントループライブラリ
      イベントループとは、無限ループを行いながら、 I/O を監視し、利用可能や I/O 完
      了等のイベントが発生するとコールバック(または、シグナル)により通知。
      I/O の監視には、 I/O multiplexing モデルを使用し、環境によって最適なシステ
      ムコール( Linux であれば、 epoll 、 FreeBSD では、 kqueue )を使用。

   libeio
    C 言語で書かれた非同期 I/O ライブラリ
      実装的には、キューとスレッドプールを使い、 I/O を非同期並行処理します。




                                                           17
Node.js のアーキテクチャ
        Java Script                          C / C++




                 Node Standard Library



                        Node Bindings
                     ( socket, http, etc )




       thread pool      event loop              DNS
V8                                                       http parser
          (libeio)        (libev)             (c-ares)




                                                                       18
libev と epoll の比較
   libev と epoll を使って、 echo サーバを実装し、比較
      してみましょう。




                                      19
epoll で echo サーバ
// メイン関数                                                      // クライアントからのイベントを処理する
int main() {                                                  void event_client (int epfd, int client, struct epoll_event ev) {
    int listener, epfd;                                           char buffer[1024];
    struct epoll_event ev;                                        int n = read(client, buffer, sizeof buffer);
    struct epoll_event events[MAX_EVENTS];                        if (n < 0) {
                                                                      perror("read");
    // サーバ起動                                                          epoll_ctl(epfd, EPOLL_CTL_DEL, client, &ev);
    listener = setup_socket();                                        close(client);
                                                                  } else if (n == 0) {
    // epollの初期化                                                      epoll_ctl(epfd, EPOLL_CTL_DEL, client, &ev);
    if ((epfd = epoll_create (MAX_EVENTS)) < 0) {                     close(client);
        die("epoll_create");                                      } else {
    }                                                                 write(client, buffer, n);
    memset(&ev, 0, sizeof ev);                                    }
    ev.events = EPOLLIN;                                      }
    ev.data.fd = listener;
    epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev);            // サーバへの接続要求イベントを処理する
                                                              void event_server (int epfd, int listener, struct epoll_event ev) {
    // 無限ループ                                                      struct sockaddr_in client_addr;
    while (1) {                                                   socklen_t client_addr_len = sizeof client_addr;
        int i;
        // 接続があるまで待つ                                              int client = accept(listener, (struct sockaddr *) &client_addr,
        int nfd = epoll_wait(epfd, events, MAX_EVENTS, -1);          &client_addr_len);
        // 接続されているクライアント数分、処理を行う                                  if (client < 0) {
        for (i = 0; i < nfd; i++) {                                   die("accept");
            // 新規接続の場合                                            }
            if (events[i].data.fd == listener) {                  setnonblocking(client);
              event_server(epfd, listener, ev);                   memset(&ev, 0, sizeof ev);
            } else {                                              ev.events = EPOLLIN | EPOLLET;
                event_client(epfd, events[i].data.fd, ev);        ev.data.fd = client;
            }                                                     epoll_ctl(epfd, EPOLL_CTL_ADD, client, &ev);
        }                                                     }
    }

    return 0;
}



epoll() を使う場合、自前で無限ループをつくり、その中で、 epoll_wait() を呼び出し、クライアント
からの接続を待つ必要があります。 epoll_wait() の最後のパラメータにマイナスの値を設定する
と、タイムアウトせずにひたすら待ちます。

                                                                                                                                    20
libev で echo サーバ
// メイン関数                                                     // クライアントからのイベントを処理する
int main() {                                                 void event_client (EV_P_ struct ev_io *w, int revents) {
    struct ev_loop *loop;                                        char buf[RCVBUFSIZE + 1];
    ev_io watcher;                                               size_t n = recv(w->fd, buf, RCVBUFSIZE, 0);
                                                                 if (n < 0) {
    // サーバ起動                                                        perror("recv");
    int listener = setup_socket();                               }
                                                                 if (n <= 0) {
    // イベントループの初期化                                                   close(w->fd);
    loop = ev_default_loop (0);                                      ev_io_stop(EV_A_ w);
    watcher.data = loop;                                             free(w);
                                                                 } else {
    // ev_ioの初期化と開始(サーバへの接続要求を監視)                                    buf[n] = '0';
    ev_io_init(&watcher, event_server, listener, EV_READ);           send(w->fd, buf, n, 0);
    ev_io_start (loop, &watcher);                                }
                                                             }
    // イベントループ開始
    ev_loop(loop, 0);                                        // サーバへの接続要求イベントを処理する
    close(listener);                                         void event_server (EV_P_ struct ev_io *w, int revents) {
    return 0;                                                    struct sockaddr_in client_addr;
}                                                                socklen_t client_addr_len = sizeof(client_addr);
                                                                 struct ev_loop *l;
                                                                 ev_io *client_watcher;

                                                                 int client = accept(w->fd, (struct sockaddr *) &client_addr,
                                                                     &client_addr_len);
                                                                 if (client < 0) {
                                                                 if (EINTR == errno) { return; }
                                                                       die("accept");
                                                                 }
                                                                 setnonblocking(client);
                                                                 client_watcher = calloc(1, sizeof(ev_io));
                                                                 l = w->data;
                                                                 // ev_ioの初期化と開始(クライアントのイベントを監視)
                                                                 ev_io_init(client_watcher, event_client, client, EV_READ);
                                                                 ev_io_start (l, client_watcher);
                                                             }


libev を使用する場合は、無限ループを作成する必要がありません。 ev_loop() で、イベント
ループとよばれる、イベントを監視するループが実行されます。イベントループに関数を設定
して、イベントを監視します。

                                                                                                                                21
int main (void) {
                                          libeio のサンプル                            // EIOスレッドで実行される
    printf ("pipe ()n");                                                         void want_poll (void) {
    if (pipe (respipe)) abort ();                                                     char dummy;
                                                                                      printf ("want_poll ()n");
    printf ("eio_init ()n");                                                         write (respipe [1], &dummy, 1);
    // libeioにwant_poll()とdone_poll()を登録。                                         }
    // libeioがpollして欲しいときに、このwant_poll()が呼び出される。
    if (eio_init (want_poll, done_poll)) abort ();                                // EIOスレッドで実行される
    do {                                                                          void done_poll (void) {
       // eio_open()呼び出し後、メインループを実行する。                                                char dummy;
        eio_open ("eio-test-file", O_RDWR | O_CREAT, 0777, 0, open_cb, "open");       printf ("done_poll ()n");
        event_loop ();                                                                read (respipe [0], &dummy, 1);
                                                                                  }
       // eio_write()呼び出し後、メインループを実行する。
        eio_write (last_fd, "aaaaaaaaaa", 10, 0, 0, res_cb, "write");             // mainスレッドのイベントループ
        event_loop ();                                                            void event_loop (void) {
                                                                                      struct pollfd pfd;
       // eio_read()呼び出し後、メインループを実行する。                                                pfd.fd     = respipe [0];
        eio_read (last_fd, 0, 8, 0, EIO_PRI_DEFAULT, read_cb, "read");                pfd.events = POLLIN;
        event_loop ();
                                                                                      printf ("nentering event loopn");
       // eio_close()呼び出し後、メインループを実行する。                                               while (eio_nreqs ()) {
        eio_close (last_fd, 0, res_cb, "close");                                         // イベントを待つ
        event_loop ();                                                                    poll (&pfd, 1, -1);
    } while (0);                                                                          // mainスレッドでeio_poll()を実行する。
    return 0;                                                                             printf ("eio_poll () = %dn", eio_poll ());
}                                                                                     }
                                                                                      printf ("leaving event loopn");
                                                                                  }
eio-test-file を作成モードで開き、 write(2) と read(2) を実行
し、 close(2) します。                                                                  // mainスレッドで実行される
                                                                                  int res_cb (eio_req *req) {
libeio は、スレッドプールにより並行処理します。 main スレッドと EIO                                            printf ("res_cb(%d|%s) = %dn", req->type,
スレッドで通信を行うために、 eio_init() で、 libeio が poll 通知できる                                         req->data ? req->data : "?", EIO_RESULT (req));
                                                                                      if (req->result < 0) perror(req->errorno);
ようにします。                                                                               return 0;
main スレッドから、 eio_open() 等の eio の I/O 関数が呼ばれると、                                    }
キューにたまります。                                                                        // mainスレッドで実行される
EIO スレッドは、キューから取り出して、システムコールを実行し、結                                                int read_cb (eio_req *req) {
果をキューに返して、 eio_poll() を呼び出すよう main スレッドに通知                                            unsigned char *buf = (unsigned char *)EIO_BUF (req);
                                                                                      printf ("read_cb = %d (%s)n",
します。                                                                                         EIO_RESULT (req),
main スレッドが eio_poll() を呼び出すと、事前に設定していた関数                                                     buf);
                                                                                      return 0;
がコールバックで呼び出されます。コールバックは、 main スレッドで                                               }
行われます。

                                                                                                                                           22
Node.js の処理フロー
   以下のような JavaScript コードを Node.js で実行し
     た場合の処理フローを説明します。
      var path = require('path'),
          fs = require('fs'),
          filepath = path.join(__dirname, 'a.txt'),
      fd = fs.openSync(filepath, 'r');
      fs.read(fd, 1024, 0, 'utf-8', function(err, str,
      bytesRead) {
          console.log(str);
      });


   カレントディレクトリから、 a.txt を読み込みコンソー
     ルに表示するプログラムです。


                                                         23
Node.js の処理フロー




                     24
Node.js の処理フロー
0. Node.js 起動時に eio_init() で node::EIOWantPoll() を libeio に登録する。
  libeio は、 poll して欲しいタイミングになると、 node::EIOWantPoll() を呼び出すようになる。
1. node_file.cc の Read() が実行される。
2. libeio の eio_submit() が実行され、 req_queue に格納される。
  req_queue には、 eio_req 構造体が保存される。
  eio_read() の場合、 {TYPE=EIO_READ, FINISH=After(), DATA->cb=callback()} が設定され
る。 After() は、 node_file.cc の関数で、 libeio で eio_poll() が呼びだされた際に
  main スレッド(イベントループ)で実行される。
  After() の中で、 DATA->cb に設定された callback() が呼ばれる。 callback() には、
  javascript の fs.read() の最後のパラメータとして
 設定された関数が設定される。今回の場合は、
  function(err, str, bytesRead) {
      console.log(str);
   }
3. EIO スレッドで、 eio_execute() が実行され、 read(2) が実行される。
5. read(2) の結果を、 req->result に設定し、 res_queue に格納する。
6. EIO スレッドで、 want_poll() が呼び出される。
7. 先程、 eio_init() で設定した、 node::EIOWantPoll() が呼び出され、 ev_async_send() により、
 イベントループへ通知される。
8. イベントループで、 node::WantPollNotifier() が呼ばれ、 eio_poll() が実行される。
9. イベントループで先程の After() が実行され、 req->result の結果が読み出される。
10. 最後に callback() が実行される。



                                                                          25
インストール
        と
    パッケージ管理




              26
Node.js のインストール( nave )
   nave とは
    Node.js のバージョン管理ソフト( Ruby でいう rvm )
    複数バージョンをひとつの環境で使える
   インストール
    1. Node.js をインストールするディレクトリを作成する。
    $ mkdir nodejs
    2. git コマンドにより、 github から nave を取得します。
    $ git clone http://github.com/isaacs/nave.git
    3. 展開された nave ディレクトリに移動し、「 nave.sh 」を実行します。
    $ cd nave
    $ ./nave.sh install latest
     「 nave.sh 」の install コマンドに対して、「 latest 」オプションを指定しています。
     「 latest 」とは、最新版を意味します。「 latest 」の代わりに
     バージョン番号を指定することもできます。
    4. 「 use 」コマンドを実行し、環境変数 PATH を設定します。
    $ ./nave.sh use latest
     「 latest 」を指定することにより、最新版のインストール先が PATH に設定されます。
     「 install 」コマンドと同様にバージョン番号を指定して、
     特定バージョンの Node.js を動作させることもできます。
    5. バージョン番号が表示されれば、インストール成功です。
    $ node -v
    v0.4.2  
                                                              27
nave のディレクトリ構成
     .
      # nave インストールディレクトリ
     |-- README.md
     |-- installed
      # 各バージョンのバイナリインストールディレクトリ
     | |-- 0.2.0
     (略)
     | |-- 0.4.2
     | | |-- bin
     | | | |-- node
     (略)
     | | | |-- npm -> ./npm@0.3.12
     (略)
     | | |-- include
     | | | `-- node
     | | |       |-- config.h
     | | |       |-- eio.h
     | | |-- lib
     | | | |-- node
     | | | | |-- npm -> ./npm@0.3.12
     |-- src
      # 各バージョンのソースコードディレクトリ
     | |-- 0.2.0
     (略)
     | |-- 0.4.2

                                       28
npm のインストール
   npm とは
    Node.js のパッケージ管理システム( Ruby でいう gem )
   インストール
    $ curl http://npmjs.org/install.sh | sh
     「 nave.sh 」の「 use 」コマンドを実行した状態で、
      npm をインストールすると Node.js と同じように
      npm も nave により管理されるようになります。




                                              29
npm 紹介
               名前                                         概要

Socket.IO                         WebSocket の node.js 実装です。チャット等のリアルタイム通信を作成するのに
http://socket.io/                 使用します。イベントループが本領発揮する領域のパッケージです。
                                  <インストール>
                                  $ npm install socket.io
Express                           Rails ライクなフレームワークです。 MVC の自動生成、ルーティング機能、モデル
http://expressjs.com/             機能等を持っています。
                                  <インストール>
                                  $ npm install express
EJS                               node.js のパッケージの中で、人気のテンプレートエンジンです。
http://embeddedjs.com/            <インストール>
                                  $ npm install ejs

jsdom                             html に対して、 dom 操作が使えるようになるパッケージです。
https://github.com/tmpvar/jsdom   <インストール>
                                  $ npm install jsdom

node-validator                      バリデーションや文字列操作、サニタイズ処理を行うパッケージです。
                                    <インストール>
https://github.com/chriso/node-validator
                                    $ npm install validator
node-oauth                          oauth 認証を行うパッケージです。
                                    <インストール>
https://github.com/ciaranj/node-oauth
                                    $ npm install oauth

node-mysql                         データベースの MySQL へ接続するためのパッケージです。
                                   <インストール>
https://github.com/felixge/node-mysql
                                   $ npm install mysql

                                                                                   30
Node.js 開発環境




                   31
Node.js 開発環境
   Node.js の開発環境を構築します。
   node­dev
    実行中のスクリプトが更新された際に自動的に再起動します。
    スクリプトを修正した時に Node.js を再起動する手間が省けます。
   node­inspector
    ブラウザ上の IDE で、 Node.js のデバッグができます。
    ※Webkit に依存するため、 Google Chrome 等の Webkit に対
     応したブラウザが必要



                                              32
node­dev
   インストール
    npm でインストールします。
    $ npm install node-dev

   使用例
    node コマンドの代わりに、 node­dev コマンドを使用します。
    console.log("hello world");


    $ node-dev hello-world.js
    9 Apr 14:06:39 - [INFO] Started
    hello world                       hello-world.js を修正すると
                                       自動的に最実行される。

    9 Apr 14:06:48 - [INFO] Started
    hello world2

                                                              33
node­inspector
   インストール
    npm でインストールします。
    $ npm install node-inspector
   使用例
    $ node-inspector
    visit http://0.0.0.0:8080/debug?port=5858 to start debugging
     まず node-inspector をコンソールから立ち上げます。

    $ node-dev --debug [ スクリプトファイル ]
     次に別のコンソールで、 node-dev (または、 node コマンド)に対して、
     「 --debug 」オプションを指定して、 Node.js を実行します。

    ブラウザで「 http://127.0.0.1:8080/debug?port=5858 」に接続します。




                                                                   34
デバッグ画面




             35
Cloud9 IDE
   ブラウザ上の IDE です。
   エディタ、シンタックスハイライト、デバッグ実行等ができます。
   インストール
    $ npm install cloud9
   実行(実行したいディレクトリで)
    $cloud9




                                     36
Cloud9 IDE  画面イメージ




                         37
Node.js (実践編)
    リアルタイム通信




                    38
Ajax vs Comet
   Ajax
    Ajax を使用して、サーバへポーリング ( 一定間隔でサーバをチェックする )
                   ◇ デメリット
                  ポーリングの間隔分の遅延が発生する
                  データ変更があるなしに関わらずチェックを行うため、 CPU やメモリを必
                    要以上に使用してしまう
                  ポーリング間隔が短すぎればネットワーク帯域やリソースを消費しすぎる
   Comet
    HTTP を使った(無理やり)プッシュ通信技術。クライアントからのリクエス
      トに対してすぐに応答せずに、サーバ上でイベントが発生したときにレス
      ポンスを返す。
                   ◇ デメリット
                  クライアントへ 2 倍のリソースが必要。
                   (ブラウザからサーバへの通常のリクエストは別 HTTP コネクショ
                     ンでやり取りするため)
                  ブラウザによって挙動が変わる場合も。。
                                                      39
WebSocket

    そこで、 WebSocket
   クライアントとサーバー間で双方向通信を実現す
     るための仕組み。
   接続の確立までは HTTP を使用し、その後は
     WebSocket 独自のプロトコルに切り替える。
   ※ 但し、現在、仕様策定中。。




                                 40
Node.js でそれぞれのサンプルを実装




                        41
チープチャット ­Ajax Poll 版 ­
HTML
<html>
<head>
      <title>Ajax Poll Chat</title>
      <script type="text/javascript" src="http://localhost/js/jquery-1.4.4.min.js"></script>
      <script type="text/javascript" src="http://localhost/js/jquery-ui-1.8.2.custom.min.js"></script>
      <script type="text/javascript" src="http://localhost/js/poll.js"></script>

</head>
<body>
<h1>Ajax Poll Chat</h1>

<!-- ログインフォーム -->
<form id="login_form">
      <span>Enter username:</span>
      <input id="user_name" type="text" size="10" value="" />
      <input id="login_button" type="button" value="Login" />
</form>

<!-- メッセージ書き込みフォーム -->
<form id="write_form">
      <span id="write_username"></span>
      <input id="write_message" type="text" size="40" value="" />
      <input id="write_button" type="button" value="Write" />
</form>

<!-- ログインアウトフォーム -->
<form id="logout_form">
      <input id="logout_button" type="button" value="Logout" />
</form>

<h2>Chat Log</h2>
<span id ="result"></span>

</body>
</html>
                                                                                                         42
サーバ側 JavaScript
var http = require('http'),
      fs = require('fs'),
      url = require('url'),
      querystring = require('querystring');

// メッセージ
var messages = [];

// http サーバの作成
http.createServer(function(req, res) {
        // index.html を表示
        if (req.url == "/") {
               fs.readFile(__dirname + '/index.html', function(err, content) {
                      res.writeHead(200, {'Content-Type':'text/html; charset=utf-8'});
                      res.end(content);
               });
        // メッセージを返す
        } else if (req.url == "/read_message.json") {
               res.writeHead(200, {'Content-Type':'application/json; charset=utf-8'});
               res.end(JSON.stringify(messages));
        // メッセージの書き込み
        } else {
               // URL パラメータの取得
               var param = querystring.parse(url.parse(req.url).query);
               // メッセージの保存
               messages.push(param);
               console.log(param);
               // メッセージを返す
               res.writeHead(200, {'Content-Type':'application/json; charset=utf-8'});
               res.end(JSON.stringify(messages));
        }
}).listen(8192, '127.0.0.1');
console.log('http://127.0.0.1:8192/');

                                                                                         43
クライアント側 JavaScript
// インターバル                                                                                // ログインボタン
var watch = null;                                                                        function set_login_button() {
                                                                                                $("#login_button").click(function(){
// 初期化処理                                                                                               read();
function init() {                                                                                      watch = setInterval(read, 5000);
       $("#write_form").hide();                                                                        $("#write_form").show();
       $("#logout_form").hide();                                                                       $("#logout_form").show();
       $("#user_name").val("");                                                                 });
}                                                                                        }

// メッセージ出力                                                                               // ログアウトボタン
function show(data) {                                                                    function set_logout_button() {
       var result = "";                                                                         $("#logout_button").click(function(){
       $.each(data, function() {                                                                       clearInterval(watch);
              if (this.write_message != null) {                                                        init();
                       result += this.user_name + ":" + this.write_message + "<br />";          });
              }                                                                          }
       });
       $("#result").html(result);                                                        $(document).ready(function() {
}                                                                                              init();
                                                                                               set_login_button();
// メッセージの読み込み                                                                                  set_logout_button();
function read() {                                                                              set_write_button();
       $.getJSON("read_message.json", null, function(data, status){                      });
             show(data);
       });
}

// メッセージ書き込み
function set_write_button() {
       $("#write_button").click(function(){
             var data = {};
             data.user_name = $("#user_name").val();
             data.write_message = $("#write_message").val();
             $.getJSON("write_message.json", data, function(data, status){
                    read();
             });
       });
}
                                                                                                                                        44
チープチャット ­Comet 版 ­
サーバ側 JavaScript
var http = require('http'),                                     // http サーバの作成
       fs = require('fs'),                                      http.createServer(function(req, res) {
       url = require('url'),                                            // index.html を表示
       querystring = require('querystring');                            if (req.url == "/") {
                                                                                fs.readFile(__dirname + '/index.html', function(err, content) {
// メッセージ                                                                                console.log("connections.length:" + connections.length);
var messages = [];                                                                      res.writeHead(200, {'Content-Type':'text/html;
// コネクション                                                                                      charset=utf-8'});
var connections = [];                                                                   res.end(content);
                                                                                });
// 全クライアントに通知                                                           // メッセージ取得
function notify() {                                                     } else if (req.url == "/read_message.json") {
       if (connections.length) {                                                // コネクションに入れて Long Poll する(レスポンスを返さない)
              var c = null;                                                     connections.push(res);
              while ((c = connections.shift()) != null) {                       console.log("connections.length:" + connections.length);
                     // メッセージを返す                                        } else {
                     c.writeHead(200,                                           // URL パラメータの取得
                     {'Content-Type':'application/json;                         var param = querystring.parse(url.parse(req.url).query);
                     charset=utf-8'});                                          // メッセージの保存
                     c.end(JSON.stringify(messages));                           messages.push(param);
              }                                                                 console.log(param);
       }                                                                        console.log("connections.length:" + connections.length);
       // 60 秒待って、全クライアントにメッセージを返す                                              // 全クライアントに通知
       setTimeout(notify, 60000);                                               notify();
}                                                                               // メッセージを返す
                                                                                res.writeHead(200,
// 60 秒待って、全クライアントにメッセージを返す                                                     {'Content-Type':'application/json; charset=utf-8'});
setTimeout(notify, 60000);                                                      res.end(JSON.stringify(messages));
                                                                        }
                                                                }).listen(8192, '127.0.0.1');
                                                                console.log('http://127.0.0.1:8192/');




                                                                                                                                               45
クライアント側 JavaScript
// 初期化処理                                                                                 // ログインボタン
function init() {                                                                        function set_login_button() {
       $("#write_form").hide();                                                                 $("#login_button").click(function(){
       $("#logout_form").hide();                                                                       read();
       $("#user_name").val("");                                                                        $("#write_form").show();
}                                                                                                      $("#logout_form").show();
                                                                                                });
// メッセージ出力                                                                               }
function show(data) {
       var result = "";                                                                  // ログアウトボタン
       $.each(data, function() {                                                         function set_logout_button() {
              if (this.write_message != null) {                                                 $("#logout_button").click(function(){
                       result += this.user_name + ":" + this.write_message + "<br />";                 init();
              }                                                                                 });
       });                                                                               }
       $("#result").html(result);
}                                                                                        $(document).ready(function() {
                                                                                               init();
// メッセージの読み込み                                                                                  set_login_button();
function read() {                                                                              set_logout_button();
       // Comet 通知用のコネクション                                                                     set_write_button();
       $.getJSON("read_message.json", null, function(data, status){                      });
             show(data);
             // 再度コネクションをはる
             read();
       });
}

// メッセージ書き込み
function set_write_button() {
       $("#write_button").click(function(){
             var data = {};
             data.user_name = $("#user_name").val();
             data.write_message = $("#write_message").val();
             $.getJSON("write_message.json", data, function(data, status){
                    show(data);
             });
       });
}
                                                                                                                                        46
チープチャット ­WebSocket 版 ­
                  var http = require('http'),
サーバ側 JavaScript
                        fs = require('fs'),
                        ws = require('websocket-server');

                  // WebSocket サーバの作成
                  var server = ws.createServer();

                  // 新規接続
                  server.addListener("connection", function(connection) {
                       console.log("connect");
                       // メッセージ受信
                       connection.addListener("message", function(message){
                              console.log(message);
                              // 全クライアントにメッセージを送る
                              server.broadcast(message);
                       });
                  });
                  // クローズ
                  server.addListener("close", function(connection) {
                       console.log("close");
                  });
                  // WebSocket サーバ待ち受け
                  server.listen(8000);

                  // http サーバの作成
                  http.createServer(function(req, res) {
                          // index.html を表示
                          fs.readFile(__dirname + '/index.html', function(err, content) {
                                 res.writeHead(200, {'Content-Type':'text/html; charset=utf-8'});
                                 res.end(content);
                          });
                  }).listen(8192, '127.0.0.1');
                  console.log('http://127.0.0.1:8192/');
                                                                                                    47
クライアント側 JavaScript
var ws = new WebSocket("ws://localhost:8000");        // ログインボタン
                                                      function set_login_button() {
// メッセージ受信時                                                 $("#login_button").click(function(){
ws.onmessage = function(message) {                                $("#write_form").show();
     var data = $.parseJSON(message.data);                        $("#logout_form").show();
     show(data);                                            });
}                                                     }

// 初期化処理                                              // ログアウトボタン
function init() {                                     function set_logout_button() {
      $("#write_form").hide();                              $("#logout_button").click(function(){
      $("#logout_form").hide();                                   init();
      $("#user_name").val("");                              });
}                                                     }

// メッセージ出力                                            $(document).ready(function() {
function show(data) {                                      init();
      var result = data.user_name + ":" +                  set_login_button();
            data.write_message + "<br />";                 set_logout_button();
      $("#result").append(result);                         set_write_button();
}                                                     });

// メッセージ書き込み
function set_write_button() {
      $("#write_button").click(function(){
            var data = {};
            data.user_name = $("#user_name").val();
            data.write_message =
                  $("#write_message").val();
            ws.send($.toJSON(data));
      });
}
                                                                                                    48
参考資料
   私のブログ「 shutdown ­h now 」 http://d.hatena.ne.jp/forest1040/
   developerWorks 「 Boost application performance using asynchronous I/O 」 
       http://www.ibm.com/developerworks/linux/library/l­async/
   「同期 I/O 」と「非同期 I/O 」の定義、とか  http://d.hatena.ne.jp/hirose31/20070815
   非同期 I/O  概説  http://www.slideshare.net/hirose31/aio
   epoll  を使った echo  サーバ  http://d.hatena.ne.jp/odz/20070507/1178558340
   libev で echo サーバを作る  http://d.hatena.ne.jp/winebarrel/20080309/p2
   C10K 問題  http://www.hyuki.com/yukiwiki/wiki.cgi?TheC10kProblem
   node.js  のソースぐらい読んでおきたい!  http://d.hatena.ne.jp/edvakf/20101207/1291556433
   C++  で node.js  ライブラリを作る・その 2   http://nodejs.g.hatena.ne.jp/edvakf/20101214/1292287495
   IT Pro Comet  プッシュ型の Web アプリケーションを作る 
        http://itpro.nikkeibp.co.jp/article/COLUMN/20080220/294242/
   node.js と WebSocket の利用シーン  http://bizria.jp/technical/nodejs­webssocket.html
   @IT Node.js でサーバサイド JavaScript 開発入門」 
       http://www.atmarkit.co.jp/fwcr/index/index_nodejs.html



                                                                                              49
ご清聴ありがとうございました。
    Node.js は、 JavaScript を使って、比較的手軽
     に非同期 I/O のシステムを実装できます。
    ◇ 本日のふりかえり
   Node.js とは
   非同期 I/O とイベントループ
   Node.js のアーキテクチャ
   インストール
   デバッグ環境
   Node.js を使ったリアルタイム通信

                                   50

More Related Content

What's hot

それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?Yoshitaka Kawashima
 
C++による数値解析の並列化手法
C++による数値解析の並列化手法C++による数値解析の並列化手法
C++による数値解析の並列化手法dc1394
 
スクリプトエンジン作って 無双する
スクリプトエンジン作って 無双するスクリプトエンジン作って 無双する
スクリプトエンジン作って 無双するKLab Inc. / Tech
 
最適輸送入門
最適輸送入門最適輸送入門
最適輸送入門joisino
 
[DL輪読会]StyleGAN-NADA: CLIP-Guided Domain Adaptation of Image Generators
[DL輪読会]StyleGAN-NADA: CLIP-Guided Domain Adaptation of Image Generators[DL輪読会]StyleGAN-NADA: CLIP-Guided Domain Adaptation of Image Generators
[DL輪読会]StyleGAN-NADA: CLIP-Guided Domain Adaptation of Image GeneratorsDeep Learning JP
 
【メタサーベイ】Vision and Language のトップ研究室/研究者
【メタサーベイ】Vision and Language のトップ研究室/研究者【メタサーベイ】Vision and Language のトップ研究室/研究者
【メタサーベイ】Vision and Language のトップ研究室/研究者cvpaper. challenge
 
オープンワールド認識 (第34回全脳アーキテクチャ若手の会 勉強会)
オープンワールド認識 (第34回全脳アーキテクチャ若手の会 勉強会)オープンワールド認識 (第34回全脳アーキテクチャ若手の会 勉強会)
オープンワールド認識 (第34回全脳アーキテクチャ若手の会 勉強会)Takuma Yagi
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなKentaro Matsui
 
[DL輪読会]EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks
[DL輪読会]EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks[DL輪読会]EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks
[DL輪読会]EfficientNet: Rethinking Model Scaling for Convolutional Neural NetworksDeep Learning JP
 
ネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分けネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分けモノビット エンジン
 
[DL輪読会]Neural Radiance Flow for 4D View Synthesis and Video Processing (NeRF...
[DL輪読会]Neural Radiance Flow for 4D View Synthesis and Video  Processing (NeRF...[DL輪読会]Neural Radiance Flow for 4D View Synthesis and Video  Processing (NeRF...
[DL輪読会]Neural Radiance Flow for 4D View Synthesis and Video Processing (NeRF...Deep Learning JP
 
テストコードの DRY と DAMP
テストコードの DRY と DAMPテストコードの DRY と DAMP
テストコードの DRY と DAMPYusuke Kagata
 
サイバーエージェントにおけるMLOpsに関する取り組み at PyDataTokyo 23
サイバーエージェントにおけるMLOpsに関する取り組み at PyDataTokyo 23サイバーエージェントにおけるMLOpsに関する取り組み at PyDataTokyo 23
サイバーエージェントにおけるMLOpsに関する取り組み at PyDataTokyo 23Masashi Shibata
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪Takuto Wada
 
SSII2022 [SS2] 少ないデータやラベルを効率的に活用する機械学習技術 〜 足りない情報をどのように補うか?〜
SSII2022 [SS2] 少ないデータやラベルを効率的に活用する機械学習技術 〜 足りない情報をどのように補うか?〜SSII2022 [SS2] 少ないデータやラベルを効率的に活用する機械学習技術 〜 足りない情報をどのように補うか?〜
SSII2022 [SS2] 少ないデータやラベルを効率的に活用する機械学習技術 〜 足りない情報をどのように補うか?〜SSII
 
勉強か?趣味か?人生か?―プログラミングコンテストとは
勉強か?趣味か?人生か?―プログラミングコンテストとは勉強か?趣味か?人生か?―プログラミングコンテストとは
勉強か?趣味か?人生か?―プログラミングコンテストとはTakuya Akiba
 
【Unity】 Behavior TreeでAIを作る
 【Unity】 Behavior TreeでAIを作る 【Unity】 Behavior TreeでAIを作る
【Unity】 Behavior TreeでAIを作るtorisoup
 
2023-03-23_Spiral.AI
2023-03-23_Spiral.AI2023-03-23_Spiral.AI
2023-03-23_Spiral.AISasakiYuichi1
 

What's hot (20)

ドロネー三角形分割
ドロネー三角形分割ドロネー三角形分割
ドロネー三角形分割
 
それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?それはYAGNIか? それとも思考停止か?
それはYAGNIか? それとも思考停止か?
 
C++による数値解析の並列化手法
C++による数値解析の並列化手法C++による数値解析の並列化手法
C++による数値解析の並列化手法
 
スクリプトエンジン作って 無双する
スクリプトエンジン作って 無双するスクリプトエンジン作って 無双する
スクリプトエンジン作って 無双する
 
最適輸送入門
最適輸送入門最適輸送入門
最適輸送入門
 
[DL輪読会]StyleGAN-NADA: CLIP-Guided Domain Adaptation of Image Generators
[DL輪読会]StyleGAN-NADA: CLIP-Guided Domain Adaptation of Image Generators[DL輪読会]StyleGAN-NADA: CLIP-Guided Domain Adaptation of Image Generators
[DL輪読会]StyleGAN-NADA: CLIP-Guided Domain Adaptation of Image Generators
 
【メタサーベイ】Vision and Language のトップ研究室/研究者
【メタサーベイ】Vision and Language のトップ研究室/研究者【メタサーベイ】Vision and Language のトップ研究室/研究者
【メタサーベイ】Vision and Language のトップ研究室/研究者
 
オープンワールド認識 (第34回全脳アーキテクチャ若手の会 勉強会)
オープンワールド認識 (第34回全脳アーキテクチャ若手の会 勉強会)オープンワールド認識 (第34回全脳アーキテクチャ若手の会 勉強会)
オープンワールド認識 (第34回全脳アーキテクチャ若手の会 勉強会)
 
テスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるなテスト文字列に「うんこ」と入れるな
テスト文字列に「うんこ」と入れるな
 
[DL輪読会]EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks
[DL輪読会]EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks[DL輪読会]EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks
[DL輪読会]EfficientNet: Rethinking Model Scaling for Convolutional Neural Networks
 
ネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分けネットワーク ゲームにおけるTCPとUDPの使い分け
ネットワーク ゲームにおけるTCPとUDPの使い分け
 
直交領域探索
直交領域探索直交領域探索
直交領域探索
 
[DL輪読会]Neural Radiance Flow for 4D View Synthesis and Video Processing (NeRF...
[DL輪読会]Neural Radiance Flow for 4D View Synthesis and Video  Processing (NeRF...[DL輪読会]Neural Radiance Flow for 4D View Synthesis and Video  Processing (NeRF...
[DL輪読会]Neural Radiance Flow for 4D View Synthesis and Video Processing (NeRF...
 
テストコードの DRY と DAMP
テストコードの DRY と DAMPテストコードの DRY と DAMP
テストコードの DRY と DAMP
 
サイバーエージェントにおけるMLOpsに関する取り組み at PyDataTokyo 23
サイバーエージェントにおけるMLOpsに関する取り組み at PyDataTokyo 23サイバーエージェントにおけるMLOpsに関する取り組み at PyDataTokyo 23
サイバーエージェントにおけるMLOpsに関する取り組み at PyDataTokyo 23
 
例外設計における大罪
例外設計における大罪例外設計における大罪
例外設計における大罪
 
SSII2022 [SS2] 少ないデータやラベルを効率的に活用する機械学習技術 〜 足りない情報をどのように補うか?〜
SSII2022 [SS2] 少ないデータやラベルを効率的に活用する機械学習技術 〜 足りない情報をどのように補うか?〜SSII2022 [SS2] 少ないデータやラベルを効率的に活用する機械学習技術 〜 足りない情報をどのように補うか?〜
SSII2022 [SS2] 少ないデータやラベルを効率的に活用する機械学習技術 〜 足りない情報をどのように補うか?〜
 
勉強か?趣味か?人生か?―プログラミングコンテストとは
勉強か?趣味か?人生か?―プログラミングコンテストとは勉強か?趣味か?人生か?―プログラミングコンテストとは
勉強か?趣味か?人生か?―プログラミングコンテストとは
 
【Unity】 Behavior TreeでAIを作る
 【Unity】 Behavior TreeでAIを作る 【Unity】 Behavior TreeでAIを作る
【Unity】 Behavior TreeでAIを作る
 
2023-03-23_Spiral.AI
2023-03-23_Spiral.AI2023-03-23_Spiral.AI
2023-03-23_Spiral.AI
 

Viewers also liked

Node.js を選ぶとき 選ばないとき
Node.js を選ぶとき 選ばないときNode.js を選ぶとき 選ばないとき
Node.js を選ぶとき 選ばないときRyunosuke SATO
 
Node.js Tutorial at Hiroshima
Node.js Tutorial at HiroshimaNode.js Tutorial at Hiroshima
Node.js Tutorial at HiroshimaYoshihiro Iwanaga
 
Node.js基礎の基礎 - Miyazaki.js vol.2
Node.js基礎の基礎 - Miyazaki.js vol.2Node.js基礎の基礎 - Miyazaki.js vol.2
Node.js基礎の基礎 - Miyazaki.js vol.2Nobuhiro Nakashima
 
Node.js×mongo dbで3年間サービス運用してみた話
Node.js×mongo dbで3年間サービス運用してみた話Node.js×mongo dbで3年間サービス運用してみた話
Node.js×mongo dbで3年間サービス運用してみた話leverages_event
 
大阪Node学園 七時限目 「ゼロからはじめるnode.js」
大阪Node学園 七時限目 「ゼロからはじめるnode.js」大阪Node学園 七時限目 「ゼロからはじめるnode.js」
大阪Node学園 七時限目 「ゼロからはじめるnode.js」Shunsuke Watanabe
 
Node.jsでサーバプログラマ デビューしよう
Node.jsでサーバプログラマ デビューしようNode.jsでサーバプログラマ デビューしよう
Node.jsでサーバプログラマ デビューしようYuusuke Takeuchi
 
Node.jsではじめるサーバ構築
Node.jsではじめるサーバ構築Node.jsではじめるサーバ構築
Node.jsではじめるサーバ構築AimingStudy
 
Java script初心者のためのnode.jsで学ぶドローン制御プログラミング
Java script初心者のためのnode.jsで学ぶドローン制御プログラミングJava script初心者のためのnode.jsで学ぶドローン制御プログラミング
Java script初心者のためのnode.jsで学ぶドローン制御プログラミング健一 茂木
 
Node Foundation Membership Overview 20160907
Node Foundation Membership Overview 20160907Node Foundation Membership Overview 20160907
Node Foundation Membership Overview 20160907NodejsFoundation
 
Nodeとフロントエンド − 知っておかなければならない、今と未来の話 −
Nodeとフロントエンド − 知っておかなければならない、今と未来の話 −Nodeとフロントエンド − 知っておかなければならない、今と未来の話 −
Nodeとフロントエンド − 知っておかなければならない、今と未来の話 −Kohei Asai
 
Node.jsに縁のない職場でnode.jsを使い始める戦術
Node.jsに縁のない職場でnode.jsを使い始める戦術Node.jsに縁のない職場でnode.jsを使い始める戦術
Node.jsに縁のない職場でnode.jsを使い始める戦術Isamu Suzuki
 
さくらのVPS で IPv4 over IPv6ルータの構築
さくらのVPS で IPv4 over IPv6ルータの構築さくらのVPS で IPv4 over IPv6ルータの構築
さくらのVPS で IPv4 over IPv6ルータの構築Tomocha Potter
 
libpgenでパケット操作
libpgenでパケット操作libpgenでパケット操作
libpgenでパケット操作slankdev
 

Viewers also liked (18)

Node.js を選ぶとき 選ばないとき
Node.js を選ぶとき 選ばないときNode.js を選ぶとき 選ばないとき
Node.js を選ぶとき 選ばないとき
 
Node.js Tutorial at Hiroshima
Node.js Tutorial at HiroshimaNode.js Tutorial at Hiroshima
Node.js Tutorial at Hiroshima
 
Node.js基礎の基礎 - Miyazaki.js vol.2
Node.js基礎の基礎 - Miyazaki.js vol.2Node.js基礎の基礎 - Miyazaki.js vol.2
Node.js基礎の基礎 - Miyazaki.js vol.2
 
Nodeについて
NodeについてNodeについて
Nodeについて
 
Node.js×mongo dbで3年間サービス運用してみた話
Node.js×mongo dbで3年間サービス運用してみた話Node.js×mongo dbで3年間サービス運用してみた話
Node.js×mongo dbで3年間サービス運用してみた話
 
Node js 入門
Node js 入門Node js 入門
Node js 入門
 
大阪Node学園 七時限目 「ゼロからはじめるnode.js」
大阪Node学園 七時限目 「ゼロからはじめるnode.js」大阪Node学園 七時限目 「ゼロからはじめるnode.js」
大阪Node学園 七時限目 「ゼロからはじめるnode.js」
 
Hello, Node.js
Hello, Node.jsHello, Node.js
Hello, Node.js
 
Node.jsでサーバプログラマ デビューしよう
Node.jsでサーバプログラマ デビューしようNode.jsでサーバプログラマ デビューしよう
Node.jsでサーバプログラマ デビューしよう
 
JUNOS: OSPF and BGP
JUNOS: OSPF and BGPJUNOS: OSPF and BGP
JUNOS: OSPF and BGP
 
Node.js Hands-On
Node.js Hands-OnNode.js Hands-On
Node.js Hands-On
 
Node.jsではじめるサーバ構築
Node.jsではじめるサーバ構築Node.jsではじめるサーバ構築
Node.jsではじめるサーバ構築
 
Java script初心者のためのnode.jsで学ぶドローン制御プログラミング
Java script初心者のためのnode.jsで学ぶドローン制御プログラミングJava script初心者のためのnode.jsで学ぶドローン制御プログラミング
Java script初心者のためのnode.jsで学ぶドローン制御プログラミング
 
Node Foundation Membership Overview 20160907
Node Foundation Membership Overview 20160907Node Foundation Membership Overview 20160907
Node Foundation Membership Overview 20160907
 
Nodeとフロントエンド − 知っておかなければならない、今と未来の話 −
Nodeとフロントエンド − 知っておかなければならない、今と未来の話 −Nodeとフロントエンド − 知っておかなければならない、今と未来の話 −
Nodeとフロントエンド − 知っておかなければならない、今と未来の話 −
 
Node.jsに縁のない職場でnode.jsを使い始める戦術
Node.jsに縁のない職場でnode.jsを使い始める戦術Node.jsに縁のない職場でnode.jsを使い始める戦術
Node.jsに縁のない職場でnode.jsを使い始める戦術
 
さくらのVPS で IPv4 over IPv6ルータの構築
さくらのVPS で IPv4 over IPv6ルータの構築さくらのVPS で IPv4 over IPv6ルータの構築
さくらのVPS で IPv4 over IPv6ルータの構築
 
libpgenでパケット操作
libpgenでパケット操作libpgenでパケット操作
libpgenでパケット操作
 

Similar to Node.js入門

イベント駆動プログラミングとI/O多重化
イベント駆動プログラミングとI/O多重化イベント駆動プログラミングとI/O多重化
イベント駆動プログラミングとI/O多重化Gosuke Miyashita
 
次世代Webコンテナ Undertowについて
次世代Webコンテナ Undertowについて次世代Webコンテナ Undertowについて
次世代Webコンテナ UndertowについてYoshimasa Tanabe
 
Swift 2.0 で変わったところ「後編」 #cswift
Swift 2.0 で変わったところ「後編」 #cswiftSwift 2.0 で変わったところ「後編」 #cswift
Swift 2.0 で変わったところ「後編」 #cswiftTomohiro Kumagai
 
東京Node学園#3 Domains & Isolates
東京Node学園#3 Domains & Isolates東京Node学園#3 Domains & Isolates
東京Node学園#3 Domains & Isolateskoichik
 
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE).NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)Tusyoshi Matsuzaki
 
Ansible troubleshooting 101_2021
Ansible troubleshooting 101_2021Ansible troubleshooting 101_2021
Ansible troubleshooting 101_2021Hideki Saito
 
Nodejuku01 ohtsu
Nodejuku01 ohtsuNodejuku01 ohtsu
Nodejuku01 ohtsuNanha Park
 
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014Takashi Yahata
 
13016 n分で作るtype scriptでnodejs
13016 n分で作るtype scriptでnodejs13016 n分で作るtype scriptでnodejs
13016 n分で作るtype scriptでnodejsTakayoshi Tanaka
 
Tizen 2.0 alpha でサポートされなかった native api
Tizen 2.0 alpha でサポートされなかった native apiTizen 2.0 alpha でサポートされなかった native api
Tizen 2.0 alpha でサポートされなかった native apiNaruto TAKAHASHI
 
フィボナッチ数列の作り方
フィボナッチ数列の作り方フィボナッチ数列の作り方
フィボナッチ数列の作り方Tomoya Kawanishi
 
PythonによるOPC-UAの利用
PythonによるOPC-UAの利用PythonによるOPC-UAの利用
PythonによるOPC-UAの利用Kioto Hirahara
 
Scala + Finagleの魅力
Scala + Finagleの魅力Scala + Finagleの魅力
Scala + Finagleの魅力Kota Mizushima
 
マスタリング DEA/NG 第2版
マスタリング DEA/NG 第2版マスタリング DEA/NG 第2版
マスタリング DEA/NG 第2版i_yudai
 
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するCEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するYoshifumi Kawai
 
Swiftからlibuvを呼び出すTIPS
Swiftからlibuvを呼び出すTIPSSwiftからlibuvを呼び出すTIPS
Swiftからlibuvを呼び出すTIPSjugemjugemjugem
 
18166746-NeverBlock-RubyKaigi2009
18166746-NeverBlock-RubyKaigi200918166746-NeverBlock-RubyKaigi2009
18166746-NeverBlock-RubyKaigi2009Muhammad Ali
 

Similar to Node.js入門 (20)

イベント駆動プログラミングとI/O多重化
イベント駆動プログラミングとI/O多重化イベント駆動プログラミングとI/O多重化
イベント駆動プログラミングとI/O多重化
 
次世代Webコンテナ Undertowについて
次世代Webコンテナ Undertowについて次世代Webコンテナ Undertowについて
次世代Webコンテナ Undertowについて
 
Swift 2.0 で変わったところ「後編」 #cswift
Swift 2.0 で変わったところ「後編」 #cswiftSwift 2.0 で変わったところ「後編」 #cswift
Swift 2.0 で変わったところ「後編」 #cswift
 
Em synchrony について
Em synchrony についてEm synchrony について
Em synchrony について
 
東京Node学園#3 Domains & Isolates
東京Node学園#3 Domains & Isolates東京Node学園#3 Domains & Isolates
東京Node学園#3 Domains & Isolates
 
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE).NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
.NET Web プログラミングにおける非同期 IO のすべて (Build Insider OFFLINE)
 
Ansible troubleshooting 101_2021
Ansible troubleshooting 101_2021Ansible troubleshooting 101_2021
Ansible troubleshooting 101_2021
 
Nodejuku01 ohtsu
Nodejuku01 ohtsuNodejuku01 ohtsu
Nodejuku01 ohtsu
 
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014
エンタープライズIT環境での OpenID Connect / SCIM の具体的実装方法 idit2014
 
13016 n分で作るtype scriptでnodejs
13016 n分で作るtype scriptでnodejs13016 n分で作るtype scriptでnodejs
13016 n分で作るtype scriptでnodejs
 
Tizen 2.0 alpha でサポートされなかった native api
Tizen 2.0 alpha でサポートされなかった native apiTizen 2.0 alpha でサポートされなかった native api
Tizen 2.0 alpha でサポートされなかった native api
 
フィボナッチ数列の作り方
フィボナッチ数列の作り方フィボナッチ数列の作り方
フィボナッチ数列の作り方
 
Driverについて
DriverについてDriverについて
Driverについて
 
PythonによるOPC-UAの利用
PythonによるOPC-UAの利用PythonによるOPC-UAの利用
PythonによるOPC-UAの利用
 
Scala + Finagleの魅力
Scala + Finagleの魅力Scala + Finagleの魅力
Scala + Finagleの魅力
 
マスタリング DEA/NG 第2版
マスタリング DEA/NG 第2版マスタリング DEA/NG 第2版
マスタリング DEA/NG 第2版
 
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭するCEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
CEDEC 2018 最速のC#の書き方 - C#大統一理論へ向けて性能的課題を払拭する
 
node-perl
node-perlnode-perl
node-perl
 
Swiftからlibuvを呼び出すTIPS
Swiftからlibuvを呼び出すTIPSSwiftからlibuvを呼び出すTIPS
Swiftからlibuvを呼び出すTIPS
 
18166746-NeverBlock-RubyKaigi2009
18166746-NeverBlock-RubyKaigi200918166746-NeverBlock-RubyKaigi2009
18166746-NeverBlock-RubyKaigi2009
 

Recently uploaded

クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfFumieNakayama
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineerYuki Kikuchi
 
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?akihisamiyanaga1
 
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)UEHARA, Tetsutaro
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版) 2024年4月作成
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版) 2024年4月作成業務で生成AIを活用したい人のための生成AI入門講座(社外公開版) 2024年4月作成
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版) 2024年4月作成Hiroshi Tomioka
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...博三 太田
 
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案sugiuralab
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfFumieNakayama
 
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)Hiroki Ichikura
 

Recently uploaded (9)

クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdfクラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
クラウドネイティブなサーバー仮想化基盤 - OpenShift Virtualization.pdf
 
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
自分史上一番早い2024振り返り〜コロナ後、仕事は通常ペースに戻ったか〜 by IoT fullstack engineer
 
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
CTO, VPoE, テックリードなどリーダーポジションに登用したくなるのはどんな人材か?
 
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
デジタル・フォレンジックの最新動向(2024年4月27日情洛会総会特別講演スライド)
 
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版) 2024年4月作成
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版) 2024年4月作成業務で生成AIを活用したい人のための生成AI入門講座(社外公開版) 2024年4月作成
業務で生成AIを活用したい人のための生成AI入門講座(社外公開版) 2024年4月作成
 
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察  ~Text-to-MusicとText-To-ImageかつImage-to-Music...
モーダル間の変換後の一致性とジャンル表を用いた解釈可能性の考察 ~Text-to-MusicとText-To-ImageかつImage-to-Music...
 
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
TataPixel: 畳の異方性を利用した切り替え可能なディスプレイの提案
 
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdfAWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
AWS の OpenShift サービス (ROSA) を使った OpenShift Virtualizationの始め方.pdf
 
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
【早稲田AI研究会 講義資料】3DスキャンとTextTo3Dのツールを知ろう!(Vol.1)
 

Node.js入門

  • 1. Node.js 入門 2011 年 4 月 16 日 森 俊夫 @ 徳島 forest1040@gmail.com http://d.hatena.ne.jp/forest1040/     1
  • 2. 自己紹介  id:forest1040  です。  徳島で、フリーランスをやってます。  Web 系エンジニアです。最近は、 Java EE(JBoss  Seam) と Ruby をよく使っています。  1年前に息子が生まれ、イクメン中です。  Node.js on Android をやってます。     2
  • 3. アジェンダ  基礎編  Node.js とは  非同期 I/O とイベントループ  Node.js のアーキテクチャ  実践編  インストール  デバッグ環境  Node.js を使ったリアルタイム通信     3
  • 4. Node.js とは  サーバサイド JavaScript  Google の V8 エンジン搭載  シングルスレッド非同期 I/O 環境  イベントループモデル     4
  • 5. シングルスレッド非同期 I/O 環境 と イベントループ     5
  • 6. 頭の体操(並行処理) 突然ですが、クイズです。  シングルスレッドで並行処理を行うには?  マルチスレッドとシングルスレッドの 並行処理の違いは?     6
  • 7. 並行処理  マルチスレッド( multithread )による並行処理 呼び元と並行に処理が行われる。  シングルスレッドでのコールバック( callback )によ る並行処理 呼び元がプロセッサを使用していないときに処理 が行われる。     7
  • 8. 非同期と同期、ノンブロッキングとブロッキング  さらにクイズです。 非同期とノンブロックの違いがわかります か?     8
  • 9. 非同期とノンブロッキング  実は、非同期( Asynchronous )とノンブロッキング ( non­blocking )は同じ意味としてよく使われて いる。  日本語に惑わされると負け。     9
  • 10. 非同期(ノンブロッキング)とは 非同期呼び出し( Asynchronous Call )と同期呼び 出し( Synchronous Call )の違い  同期呼び出し( Synchronous Call ) 通常メソッドを呼び出すとメソッド内の処理が完了 するまで、呼び出し元には戻ってこない。このよう なメソッド呼び出しのこと。  非同期呼び出し( Asynchronous Call ) メソッドを呼び出した瞬間に呼び出し元に処理が 戻ってくるような呼び出しのこと。非同期で呼び 出されたメソッドは、環境によって処理されるタイ ミングが変わる。 まさしくノンブロッキング     10
  • 11. ノンブロッキング I/O とは ブロッキング I/O ( blocking I/O )とノンブロッキン グ I/O ( non­blocking I/O )の違い  ブロッキング I/O ( blocking I/O ) データ処理が完了するまで待たされること。  ノンブロッキング I/O ( non­blocking I/O ) データ処理の完了を待たされずに、他の処理を行 えること。     11
  • 12. I/O モデル W. Richard Stevens の 「 UNIX ネットワークプログラミング 第 2 版 Vol.1 」  Synchronous I/O Operation ( 同期 I/O 操作 )  blocking I/O ( ブロッキング I/O)  non­blocking I/O ( 非ブロッキング I/O)  I/O multiplexing (I/O 多重化 )  signal driven I/O ( シグナル駆動 I/O)  Asynchronous I/O Operation ( 非同期 I/O 操作 )  Asynchronous I/O ( 非同期 I/O)     12
  • 13. Boost application performance using  asynchronous I/O 引用: Boost application performance using asynchronous I/O http://www­128.ibm.com/developerworks/linux/library/l­async/     13
  • 14. Boost application performance using  asynchronous I/O この図では、 I/O multiplexing が Asynchronous に分類されている。 引用: Boost application performance using asynchronous I/O http://www­128.ibm.com/developerworks/linux/library/l­async/     14
  • 15. C10K 問題  ハードウェアの性能上は問題がなくても、あまりにもクライアントの数 が多くなるとサーバがパンクする問題のこと。 ◇ 解決方法 1. 各スレッドが複数のクライアントを受け付ける。 そしてノンブロッキン グ I/O  と レベル・トリガ型の完了通知 (level­triggered readiness  notification) を利用する。 2. 各スレッドが複数のクライアントを受け付ける。 そしてノンブロッキン グ I/O  と 変更型の完了通知 (readiness change notification) を利 用する。 3. 各スレッドが複数のクライアントを受けつける。 そして非同期 I/O  を 使う。 4. 各スレッドが一つのクライアントを受けつける。 そしてブロッキング I/O  を使う。 5. サーバのコードをカーネルに組込む。     15
  • 16. マルチスレッド vs  ノンブロッキング  apache  マルチスレッドモデル (C10K の解決案 No.4)  nginx  ノンブロッキング( C10K の解決案 No.1 ) 1秒あたりの処理リクエスト数 メモリ使用量 nginx  は、最高で秒間 10,000 リクエストを処理する。同時接続数を 増やしても、リクエスト処理数は少々減る程度  apache の場合、同時接続数を増やすと著しくリクエスト処理数が減 る。しかも、メモリ使用量が同時接続数に比例して増える。 つまり、同時接続数が多い場合は、ノンブロッキングが有利!! 参照: http://blog.webfaction.com/a­little­holiday­present     16
  • 17. Node.js の場合  「 3.  各スレッドが複数のクライアントを受けつける。 そして 非同期 I/O  を使う」を採用  「 libev 」と「 libeio 」を使用して、非同期 I/O 環境を実装  libev C 言語で書かれたイベントループライブラリ イベントループとは、無限ループを行いながら、 I/O を監視し、利用可能や I/O 完 了等のイベントが発生するとコールバック(または、シグナル)により通知。 I/O の監視には、 I/O multiplexing モデルを使用し、環境によって最適なシステ ムコール( Linux であれば、 epoll 、 FreeBSD では、 kqueue )を使用。  libeio C 言語で書かれた非同期 I/O ライブラリ 実装的には、キューとスレッドプールを使い、 I/O を非同期並行処理します。     17
  • 18. Node.js のアーキテクチャ Java Script C / C++ Node Standard Library Node Bindings ( socket, http, etc ) thread pool event loop DNS V8 http parser (libeio) (libev) (c-ares)     18
  • 19. libev と epoll の比較  libev と epoll を使って、 echo サーバを実装し、比較 してみましょう。     19
  • 20. epoll で echo サーバ // メイン関数 // クライアントからのイベントを処理する int main() { void event_client (int epfd, int client, struct epoll_event ev) { int listener, epfd; char buffer[1024]; struct epoll_event ev; int n = read(client, buffer, sizeof buffer); struct epoll_event events[MAX_EVENTS]; if (n < 0) { perror("read"); // サーバ起動 epoll_ctl(epfd, EPOLL_CTL_DEL, client, &ev); listener = setup_socket(); close(client); } else if (n == 0) { // epollの初期化 epoll_ctl(epfd, EPOLL_CTL_DEL, client, &ev); if ((epfd = epoll_create (MAX_EVENTS)) < 0) { close(client); die("epoll_create"); } else { } write(client, buffer, n); memset(&ev, 0, sizeof ev); } ev.events = EPOLLIN; } ev.data.fd = listener; epoll_ctl(epfd, EPOLL_CTL_ADD, listener, &ev); // サーバへの接続要求イベントを処理する void event_server (int epfd, int listener, struct epoll_event ev) { // 無限ループ struct sockaddr_in client_addr; while (1) { socklen_t client_addr_len = sizeof client_addr; int i; // 接続があるまで待つ int client = accept(listener, (struct sockaddr *) &client_addr, int nfd = epoll_wait(epfd, events, MAX_EVENTS, -1); &client_addr_len); // 接続されているクライアント数分、処理を行う if (client < 0) { for (i = 0; i < nfd; i++) { die("accept"); // 新規接続の場合 } if (events[i].data.fd == listener) { setnonblocking(client); event_server(epfd, listener, ev); memset(&ev, 0, sizeof ev); } else { ev.events = EPOLLIN | EPOLLET; event_client(epfd, events[i].data.fd, ev); ev.data.fd = client; } epoll_ctl(epfd, EPOLL_CTL_ADD, client, &ev); } } } return 0; } epoll() を使う場合、自前で無限ループをつくり、その中で、 epoll_wait() を呼び出し、クライアント からの接続を待つ必要があります。 epoll_wait() の最後のパラメータにマイナスの値を設定する と、タイムアウトせずにひたすら待ちます。     20
  • 21. libev で echo サーバ // メイン関数 // クライアントからのイベントを処理する int main() { void event_client (EV_P_ struct ev_io *w, int revents) { struct ev_loop *loop; char buf[RCVBUFSIZE + 1]; ev_io watcher; size_t n = recv(w->fd, buf, RCVBUFSIZE, 0); if (n < 0) { // サーバ起動 perror("recv"); int listener = setup_socket(); } if (n <= 0) { // イベントループの初期化 close(w->fd); loop = ev_default_loop (0); ev_io_stop(EV_A_ w); watcher.data = loop; free(w); } else { // ev_ioの初期化と開始(サーバへの接続要求を監視) buf[n] = '0'; ev_io_init(&watcher, event_server, listener, EV_READ); send(w->fd, buf, n, 0); ev_io_start (loop, &watcher); } } // イベントループ開始 ev_loop(loop, 0); // サーバへの接続要求イベントを処理する close(listener); void event_server (EV_P_ struct ev_io *w, int revents) { return 0; struct sockaddr_in client_addr; } socklen_t client_addr_len = sizeof(client_addr); struct ev_loop *l; ev_io *client_watcher; int client = accept(w->fd, (struct sockaddr *) &client_addr, &client_addr_len); if (client < 0) { if (EINTR == errno) { return; } die("accept"); } setnonblocking(client); client_watcher = calloc(1, sizeof(ev_io)); l = w->data; // ev_ioの初期化と開始(クライアントのイベントを監視) ev_io_init(client_watcher, event_client, client, EV_READ); ev_io_start (l, client_watcher); } libev を使用する場合は、無限ループを作成する必要がありません。 ev_loop() で、イベント ループとよばれる、イベントを監視するループが実行されます。イベントループに関数を設定 して、イベントを監視します。     21
  • 22. int main (void) { libeio のサンプル // EIOスレッドで実行される printf ("pipe ()n"); void want_poll (void) { if (pipe (respipe)) abort (); char dummy; printf ("want_poll ()n"); printf ("eio_init ()n"); write (respipe [1], &dummy, 1); // libeioにwant_poll()とdone_poll()を登録。 } // libeioがpollして欲しいときに、このwant_poll()が呼び出される。 if (eio_init (want_poll, done_poll)) abort (); // EIOスレッドで実行される do { void done_poll (void) { // eio_open()呼び出し後、メインループを実行する。 char dummy; eio_open ("eio-test-file", O_RDWR | O_CREAT, 0777, 0, open_cb, "open"); printf ("done_poll ()n"); event_loop (); read (respipe [0], &dummy, 1); } // eio_write()呼び出し後、メインループを実行する。 eio_write (last_fd, "aaaaaaaaaa", 10, 0, 0, res_cb, "write"); // mainスレッドのイベントループ event_loop (); void event_loop (void) { struct pollfd pfd; // eio_read()呼び出し後、メインループを実行する。 pfd.fd = respipe [0]; eio_read (last_fd, 0, 8, 0, EIO_PRI_DEFAULT, read_cb, "read"); pfd.events = POLLIN; event_loop (); printf ("nentering event loopn"); // eio_close()呼び出し後、メインループを実行する。 while (eio_nreqs ()) { eio_close (last_fd, 0, res_cb, "close"); // イベントを待つ event_loop (); poll (&pfd, 1, -1); } while (0); // mainスレッドでeio_poll()を実行する。 return 0; printf ("eio_poll () = %dn", eio_poll ()); } } printf ("leaving event loopn"); } eio-test-file を作成モードで開き、 write(2) と read(2) を実行 し、 close(2) します。 // mainスレッドで実行される int res_cb (eio_req *req) { libeio は、スレッドプールにより並行処理します。 main スレッドと EIO printf ("res_cb(%d|%s) = %dn", req->type, スレッドで通信を行うために、 eio_init() で、 libeio が poll 通知できる req->data ? req->data : "?", EIO_RESULT (req)); if (req->result < 0) perror(req->errorno); ようにします。 return 0; main スレッドから、 eio_open() 等の eio の I/O 関数が呼ばれると、 } キューにたまります。 // mainスレッドで実行される EIO スレッドは、キューから取り出して、システムコールを実行し、結 int read_cb (eio_req *req) { 果をキューに返して、 eio_poll() を呼び出すよう main スレッドに通知 unsigned char *buf = (unsigned char *)EIO_BUF (req); printf ("read_cb = %d (%s)n", します。 EIO_RESULT (req), main スレッドが eio_poll() を呼び出すと、事前に設定していた関数 buf); return 0; がコールバックで呼び出されます。コールバックは、 main スレッドで } 行われます。     22
  • 23. Node.js の処理フロー  以下のような JavaScript コードを Node.js で実行し た場合の処理フローを説明します。 var path = require('path'), fs = require('fs'), filepath = path.join(__dirname, 'a.txt'), fd = fs.openSync(filepath, 'r'); fs.read(fd, 1024, 0, 'utf-8', function(err, str, bytesRead) { console.log(str); });  カレントディレクトリから、 a.txt を読み込みコンソー ルに表示するプログラムです。     23
  • 25. Node.js の処理フロー 0. Node.js 起動時に eio_init() で node::EIOWantPoll() を libeio に登録する。   libeio は、 poll して欲しいタイミングになると、 node::EIOWantPoll() を呼び出すようになる。 1. node_file.cc の Read() が実行される。 2. libeio の eio_submit() が実行され、 req_queue に格納される。   req_queue には、 eio_req 構造体が保存される。   eio_read() の場合、 {TYPE=EIO_READ, FINISH=After(), DATA->cb=callback()} が設定され る。 After() は、 node_file.cc の関数で、 libeio で eio_poll() が呼びだされた際に   main スレッド(イベントループ)で実行される。   After() の中で、 DATA->cb に設定された callback() が呼ばれる。 callback() には、   javascript の fs.read() の最後のパラメータとして  設定された関数が設定される。今回の場合は、   function(err, str, bytesRead) { console.log(str); } 3. EIO スレッドで、 eio_execute() が実行され、 read(2) が実行される。 5. read(2) の結果を、 req->result に設定し、 res_queue に格納する。 6. EIO スレッドで、 want_poll() が呼び出される。 7. 先程、 eio_init() で設定した、 node::EIOWantPoll() が呼び出され、 ev_async_send() により、  イベントループへ通知される。 8. イベントループで、 node::WantPollNotifier() が呼ばれ、 eio_poll() が実行される。 9. イベントループで先程の After() が実行され、 req->result の結果が読み出される。 10. 最後に callback() が実行される。     25
  • 26. インストール と パッケージ管理     26
  • 27. Node.js のインストール( nave )  nave とは Node.js のバージョン管理ソフト( Ruby でいう rvm ) 複数バージョンをひとつの環境で使える  インストール 1. Node.js をインストールするディレクトリを作成する。 $ mkdir nodejs 2. git コマンドにより、 github から nave を取得します。 $ git clone http://github.com/isaacs/nave.git 3. 展開された nave ディレクトリに移動し、「 nave.sh 」を実行します。 $ cd nave $ ./nave.sh install latest  「 nave.sh 」の install コマンドに対して、「 latest 」オプションを指定しています。  「 latest 」とは、最新版を意味します。「 latest 」の代わりに  バージョン番号を指定することもできます。 4. 「 use 」コマンドを実行し、環境変数 PATH を設定します。 $ ./nave.sh use latest  「 latest 」を指定することにより、最新版のインストール先が PATH に設定されます。  「 install 」コマンドと同様にバージョン番号を指定して、  特定バージョンの Node.js を動作させることもできます。 5. バージョン番号が表示されれば、インストール成功です。 $ node -v v0.4.2       27
  • 28. nave のディレクトリ構成 . # nave インストールディレクトリ |-- README.md |-- installed # 各バージョンのバイナリインストールディレクトリ | |-- 0.2.0 (略) | |-- 0.4.2 | | |-- bin | | | |-- node (略) | | | |-- npm -> ./npm@0.3.12 (略) | | |-- include | | | `-- node | | | |-- config.h | | | |-- eio.h | | |-- lib | | | |-- node | | | | |-- npm -> ./npm@0.3.12 |-- src # 各バージョンのソースコードディレクトリ | |-- 0.2.0 (略) | |-- 0.4.2     28
  • 29. npm のインストール  npm とは Node.js のパッケージ管理システム( Ruby でいう gem )  インストール $ curl http://npmjs.org/install.sh | sh  「 nave.sh 」の「 use 」コマンドを実行した状態で、   npm をインストールすると Node.js と同じように   npm も nave により管理されるようになります。     29
  • 30. npm 紹介 名前 概要 Socket.IO WebSocket の node.js 実装です。チャット等のリアルタイム通信を作成するのに http://socket.io/ 使用します。イベントループが本領発揮する領域のパッケージです。 <インストール> $ npm install socket.io Express Rails ライクなフレームワークです。 MVC の自動生成、ルーティング機能、モデル http://expressjs.com/ 機能等を持っています。 <インストール> $ npm install express EJS node.js のパッケージの中で、人気のテンプレートエンジンです。 http://embeddedjs.com/ <インストール> $ npm install ejs jsdom html に対して、 dom 操作が使えるようになるパッケージです。 https://github.com/tmpvar/jsdom <インストール> $ npm install jsdom node-validator バリデーションや文字列操作、サニタイズ処理を行うパッケージです。 <インストール> https://github.com/chriso/node-validator $ npm install validator node-oauth oauth 認証を行うパッケージです。 <インストール> https://github.com/ciaranj/node-oauth $ npm install oauth node-mysql データベースの MySQL へ接続するためのパッケージです。 <インストール> https://github.com/felixge/node-mysql $ npm install mysql     30
  • 32. Node.js 開発環境  Node.js の開発環境を構築します。  node­dev 実行中のスクリプトが更新された際に自動的に再起動します。 スクリプトを修正した時に Node.js を再起動する手間が省けます。  node­inspector ブラウザ上の IDE で、 Node.js のデバッグができます。 ※Webkit に依存するため、 Google Chrome 等の Webkit に対 応したブラウザが必要     32
  • 33. node­dev  インストール npm でインストールします。 $ npm install node-dev  使用例 node コマンドの代わりに、 node­dev コマンドを使用します。 console.log("hello world"); $ node-dev hello-world.js 9 Apr 14:06:39 - [INFO] Started hello world hello-world.js を修正すると 自動的に最実行される。 9 Apr 14:06:48 - [INFO] Started hello world2     33
  • 34. node­inspector  インストール npm でインストールします。 $ npm install node-inspector  使用例 $ node-inspector visit http://0.0.0.0:8080/debug?port=5858 to start debugging  まず node-inspector をコンソールから立ち上げます。 $ node-dev --debug [ スクリプトファイル ]  次に別のコンソールで、 node-dev (または、 node コマンド)に対して、  「 --debug 」オプションを指定して、 Node.js を実行します。 ブラウザで「 http://127.0.0.1:8080/debug?port=5858 」に接続します。     34
  • 36. Cloud9 IDE  ブラウザ上の IDE です。  エディタ、シンタックスハイライト、デバッグ実行等ができます。  インストール $ npm install cloud9  実行(実行したいディレクトリで) $cloud9     36
  • 38. Node.js (実践編) リアルタイム通信     38
  • 39. Ajax vs Comet  Ajax Ajax を使用して、サーバへポーリング ( 一定間隔でサーバをチェックする ) ◇ デメリット  ポーリングの間隔分の遅延が発生する  データ変更があるなしに関わらずチェックを行うため、 CPU やメモリを必 要以上に使用してしまう  ポーリング間隔が短すぎればネットワーク帯域やリソースを消費しすぎる  Comet HTTP を使った(無理やり)プッシュ通信技術。クライアントからのリクエス トに対してすぐに応答せずに、サーバ上でイベントが発生したときにレス ポンスを返す。 ◇ デメリット  クライアントへ 2 倍のリソースが必要。 (ブラウザからサーバへの通常のリクエストは別 HTTP コネクショ ンでやり取りするため)  ブラウザによって挙動が変わる場合も。。     39
  • 40. WebSocket そこで、 WebSocket  クライアントとサーバー間で双方向通信を実現す るための仕組み。  接続の確立までは HTTP を使用し、その後は WebSocket 独自のプロトコルに切り替える。  ※ 但し、現在、仕様策定中。。     40
  • 42. チープチャット ­Ajax Poll 版 ­ HTML <html> <head> <title>Ajax Poll Chat</title> <script type="text/javascript" src="http://localhost/js/jquery-1.4.4.min.js"></script> <script type="text/javascript" src="http://localhost/js/jquery-ui-1.8.2.custom.min.js"></script> <script type="text/javascript" src="http://localhost/js/poll.js"></script> </head> <body> <h1>Ajax Poll Chat</h1> <!-- ログインフォーム --> <form id="login_form"> <span>Enter username:</span> <input id="user_name" type="text" size="10" value="" /> <input id="login_button" type="button" value="Login" /> </form> <!-- メッセージ書き込みフォーム --> <form id="write_form"> <span id="write_username"></span> <input id="write_message" type="text" size="40" value="" /> <input id="write_button" type="button" value="Write" /> </form> <!-- ログインアウトフォーム --> <form id="logout_form"> <input id="logout_button" type="button" value="Logout" /> </form> <h2>Chat Log</h2> <span id ="result"></span> </body> </html>     42
  • 43. サーバ側 JavaScript var http = require('http'), fs = require('fs'), url = require('url'), querystring = require('querystring'); // メッセージ var messages = []; // http サーバの作成 http.createServer(function(req, res) { // index.html を表示 if (req.url == "/") { fs.readFile(__dirname + '/index.html', function(err, content) { res.writeHead(200, {'Content-Type':'text/html; charset=utf-8'}); res.end(content); }); // メッセージを返す } else if (req.url == "/read_message.json") { res.writeHead(200, {'Content-Type':'application/json; charset=utf-8'}); res.end(JSON.stringify(messages)); // メッセージの書き込み } else { // URL パラメータの取得 var param = querystring.parse(url.parse(req.url).query); // メッセージの保存 messages.push(param); console.log(param); // メッセージを返す res.writeHead(200, {'Content-Type':'application/json; charset=utf-8'}); res.end(JSON.stringify(messages)); } }).listen(8192, '127.0.0.1'); console.log('http://127.0.0.1:8192/');     43
  • 44. クライアント側 JavaScript // インターバル // ログインボタン var watch = null; function set_login_button() { $("#login_button").click(function(){ // 初期化処理 read(); function init() { watch = setInterval(read, 5000); $("#write_form").hide(); $("#write_form").show(); $("#logout_form").hide(); $("#logout_form").show(); $("#user_name").val(""); }); } } // メッセージ出力 // ログアウトボタン function show(data) { function set_logout_button() { var result = ""; $("#logout_button").click(function(){ $.each(data, function() { clearInterval(watch); if (this.write_message != null) { init(); result += this.user_name + ":" + this.write_message + "<br />"; }); } } }); $("#result").html(result); $(document).ready(function() { } init(); set_login_button(); // メッセージの読み込み set_logout_button(); function read() { set_write_button(); $.getJSON("read_message.json", null, function(data, status){ }); show(data); }); } // メッセージ書き込み function set_write_button() { $("#write_button").click(function(){ var data = {}; data.user_name = $("#user_name").val(); data.write_message = $("#write_message").val(); $.getJSON("write_message.json", data, function(data, status){ read(); }); }); }     44
  • 45. チープチャット ­Comet 版 ­ サーバ側 JavaScript var http = require('http'), // http サーバの作成 fs = require('fs'), http.createServer(function(req, res) { url = require('url'), // index.html を表示 querystring = require('querystring'); if (req.url == "/") { fs.readFile(__dirname + '/index.html', function(err, content) { // メッセージ console.log("connections.length:" + connections.length); var messages = []; res.writeHead(200, {'Content-Type':'text/html; // コネクション charset=utf-8'}); var connections = []; res.end(content); }); // 全クライアントに通知 // メッセージ取得 function notify() { } else if (req.url == "/read_message.json") { if (connections.length) { // コネクションに入れて Long Poll する(レスポンスを返さない) var c = null; connections.push(res); while ((c = connections.shift()) != null) { console.log("connections.length:" + connections.length); // メッセージを返す } else { c.writeHead(200, // URL パラメータの取得 {'Content-Type':'application/json; var param = querystring.parse(url.parse(req.url).query); charset=utf-8'}); // メッセージの保存 c.end(JSON.stringify(messages)); messages.push(param); } console.log(param); } console.log("connections.length:" + connections.length); // 60 秒待って、全クライアントにメッセージを返す // 全クライアントに通知 setTimeout(notify, 60000); notify(); } // メッセージを返す res.writeHead(200, // 60 秒待って、全クライアントにメッセージを返す {'Content-Type':'application/json; charset=utf-8'}); setTimeout(notify, 60000); res.end(JSON.stringify(messages)); } }).listen(8192, '127.0.0.1'); console.log('http://127.0.0.1:8192/');     45
  • 46. クライアント側 JavaScript // 初期化処理 // ログインボタン function init() { function set_login_button() { $("#write_form").hide(); $("#login_button").click(function(){ $("#logout_form").hide(); read(); $("#user_name").val(""); $("#write_form").show(); } $("#logout_form").show(); }); // メッセージ出力 } function show(data) { var result = ""; // ログアウトボタン $.each(data, function() { function set_logout_button() { if (this.write_message != null) { $("#logout_button").click(function(){ result += this.user_name + ":" + this.write_message + "<br />"; init(); } }); }); } $("#result").html(result); } $(document).ready(function() { init(); // メッセージの読み込み set_login_button(); function read() { set_logout_button(); // Comet 通知用のコネクション set_write_button(); $.getJSON("read_message.json", null, function(data, status){ }); show(data); // 再度コネクションをはる read(); }); } // メッセージ書き込み function set_write_button() { $("#write_button").click(function(){ var data = {}; data.user_name = $("#user_name").val(); data.write_message = $("#write_message").val(); $.getJSON("write_message.json", data, function(data, status){ show(data); }); }); }     46
  • 47. チープチャット ­WebSocket 版 ­ var http = require('http'), サーバ側 JavaScript fs = require('fs'), ws = require('websocket-server'); // WebSocket サーバの作成 var server = ws.createServer(); // 新規接続 server.addListener("connection", function(connection) { console.log("connect"); // メッセージ受信 connection.addListener("message", function(message){ console.log(message); // 全クライアントにメッセージを送る server.broadcast(message); }); }); // クローズ server.addListener("close", function(connection) { console.log("close"); }); // WebSocket サーバ待ち受け server.listen(8000); // http サーバの作成 http.createServer(function(req, res) { // index.html を表示 fs.readFile(__dirname + '/index.html', function(err, content) { res.writeHead(200, {'Content-Type':'text/html; charset=utf-8'}); res.end(content); }); }).listen(8192, '127.0.0.1'); console.log('http://127.0.0.1:8192/');     47
  • 48. クライアント側 JavaScript var ws = new WebSocket("ws://localhost:8000"); // ログインボタン function set_login_button() { // メッセージ受信時 $("#login_button").click(function(){ ws.onmessage = function(message) { $("#write_form").show(); var data = $.parseJSON(message.data); $("#logout_form").show(); show(data); }); } } // 初期化処理 // ログアウトボタン function init() { function set_logout_button() { $("#write_form").hide(); $("#logout_button").click(function(){ $("#logout_form").hide(); init(); $("#user_name").val(""); }); } } // メッセージ出力 $(document).ready(function() { function show(data) { init(); var result = data.user_name + ":" + set_login_button(); data.write_message + "<br />"; set_logout_button(); $("#result").append(result); set_write_button(); } }); // メッセージ書き込み function set_write_button() { $("#write_button").click(function(){ var data = {}; data.user_name = $("#user_name").val(); data.write_message = $("#write_message").val(); ws.send($.toJSON(data)); }); }     48
  • 49. 参考資料  私のブログ「 shutdown ­h now 」 http://d.hatena.ne.jp/forest1040/  developerWorks 「 Boost application performance using asynchronous I/O 」  http://www.ibm.com/developerworks/linux/library/l­async/  「同期 I/O 」と「非同期 I/O 」の定義、とか  http://d.hatena.ne.jp/hirose31/20070815  非同期 I/O  概説  http://www.slideshare.net/hirose31/aio  epoll  を使った echo  サーバ  http://d.hatena.ne.jp/odz/20070507/1178558340  libev で echo サーバを作る  http://d.hatena.ne.jp/winebarrel/20080309/p2  C10K 問題  http://www.hyuki.com/yukiwiki/wiki.cgi?TheC10kProblem  node.js  のソースぐらい読んでおきたい!  http://d.hatena.ne.jp/edvakf/20101207/1291556433  C++  で node.js  ライブラリを作る・その 2   http://nodejs.g.hatena.ne.jp/edvakf/20101214/1292287495  IT Pro Comet  プッシュ型の Web アプリケーションを作る  http://itpro.nikkeibp.co.jp/article/COLUMN/20080220/294242/  node.js と WebSocket の利用シーン  http://bizria.jp/technical/nodejs­webssocket.html  @IT Node.js でサーバサイド JavaScript 開発入門」  http://www.atmarkit.co.jp/fwcr/index/index_nodejs.html     49
  • 50. ご清聴ありがとうございました。 Node.js は、 JavaScript を使って、比較的手軽 に非同期 I/O のシステムを実装できます。 ◇ 本日のふりかえり  Node.js とは  非同期 I/O とイベントループ  Node.js のアーキテクチャ  インストール  デバッグ環境  Node.js を使ったリアルタイム通信     50