Publicidad

Node.js入門

コンピューターソフトウェア (自営) en Freelancer
17 de Apr de 2011
Publicidad

Más contenido relacionado

Presentaciones para ti(20)

Publicidad

Último(20)

Publicidad

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
  24. Node.js の処理フロー     24
  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
  31. Node.js 開発環境     31
  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
  35. デバッグ画面     35
  36. Cloud9 IDE  ブラウザ上の IDE です。  エディタ、シンタックスハイライト、デバッグ実行等ができます。  インストール $ npm install cloud9  実行(実行したいディレクトリで) $cloud9     36
  37. Cloud9 IDE  画面イメージ     37
  38. Node.js (実践編) リアルタイム通信     38
  39. Ajax vs Comet  Ajax Ajax を使用して、サーバへポーリング ( 一定間隔でサーバをチェックする ) ◇ デメリット  ポーリングの間隔分の遅延が発生する  データ変更があるなしに関わらずチェックを行うため、 CPU やメモリを必 要以上に使用してしまう  ポーリング間隔が短すぎればネットワーク帯域やリソースを消費しすぎる  Comet HTTP を使った(無理やり)プッシュ通信技術。クライアントからのリクエス トに対してすぐに応答せずに、サーバ上でイベントが発生したときにレス ポンスを返す。 ◇ デメリット  クライアントへ 2 倍のリソースが必要。 (ブラウザからサーバへの通常のリクエストは別 HTTP コネクショ ンでやり取りするため)  ブラウザによって挙動が変わる場合も。。     39
  40. WebSocket そこで、 WebSocket  クライアントとサーバー間で双方向通信を実現す るための仕組み。  接続の確立までは HTTP を使用し、その後は WebSocket 独自のプロトコルに切り替える。  ※ 但し、現在、仕様策定中。。     40
  41. Node.js でそれぞれのサンプルを実装     41
  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
Publicidad