Hatena::Groupcside

Cside::StudyMemo このページをアンテナに追加 RSSフィード

メインブログに書くまでもない自分用メモを垂れ流す。日々是勉強也。

カテゴリー

2011-03-15

[][]作りながらnode.jsを復習

ttp://d.hatena.ne.jp/sugyan/20110313/1300025780 をみながらnode.jsを復習。

var access_token = 'XXXXXXXXXXXXXXXX';
var tag          = 'prayforjapan';

var http     = require('http');
var https    = require('https');
var socketIo = require('socket.io');

var flg = false,
var recent;

// node.jsでサーバー立てるときは http.createServer
// app.onrequest = function() { ... }; と同じ
var app = http.createServer( function(req, res) {
    // Realtime StreamはAPI提供側からPOSTで通知される
    if (req.method == 'POST') {  // 本当はヘッダの検証とかする必要あり
        if (! flg) {
            flg = true;
            https.get({  // (1)
                host: 'api.instagram.con',
                path: '/v1/tags/' + tag + '/media/recent?access_token=' + access_token
            }, function(res) {
                var body = '';
                res.on('data', function() {
                    body += data.toString();
                });
                res.on('end', function() {
                    recent = JSON.parse(body);
                    socket.broadcast(recent);  // undefined socket
                    flg = false;
                });
            });
        }
        res.end();
    }
    else {
        if (req.url != '/' + tag) {
            res.statusCode = 404;
            res.end;
            return;
        }
        // Webページの用意
        res.writeHead(200, {'Content-Type': 'text/html'});  // (2)
        res.write(''); // 表示はクライアントでやるので内容はどうでもいい
        res.end();
    }
});
app.listen(80):

var socket = socketIo.listen(app);
socket.on('connection', function(client) {
    if (recent) {
        client.send(recent);
        // clientからのmsgは受けないパターン。
     // 接続を確立してからずっと監視し続けるというよりは、同期的な例。
    }
})

client側の実装はこう

// WSのアクセスはsocket単体でOK
var socket = new io.Socket('64.30.139.104');  // (5)
socket.on("message", function(msg) {
    ....
});
socket.connect();

(1) node.jsにおけるHTTP Request処理

一番簡単なのはこれ。$ua->get($url) みたいなもん。

var http = require('http');

http.get({
    host: 'localhost',
    path: '/',
}, function(res) {
    var body = ""
    res.on('data', function(data) {
        body += data;
    });
    res.on('end', function() {
        console.log(body);
    });
});

$ua->request(GET => $url) にあたるのがこれ。こちらは明示的にend()する必要あり。

var req = http.request({
    ....
}, function(res) {
    ....
});
req.end();

// equals
var req = http.request({
    ....
});
req.end();

req.on('response', function(res) {
    res.on('data', function (data) { });
    res.on('end', function() { });
});

POSTだとこう。reqにwriteする。

var req = http.request({
    ....
}, function(res) {
    ....
});
req.write('data\n');
req.end();

ドキュメントによると、http.requestはClientRequestクラスのインスタンスを返すらしい。このインスタンスは別の方法によっても生成できて、以下のように書く:

var client = http.createClient(port, host);
var request = client.request(
    'GET',
    path,
    { host: u.hostname }
);

(2) 基本的なレスポンス

Hello Worldを思い出す。

var server = http.createServer(
    function (request, response) {

        response.writeHead(200, {'Content-Type': 'text/plain'});
        response.write('Hello World!!\n');
        response.end();
    }
).listen(8124);

(3) ファイルを読み込む・書きこむ

ReadStream、WriteStreamという概念。

ttp://d.hatena.ne.jp/yamamucho/20110220/1298191467

(4) Expressを使うか否かの境目

サーバー側ではclientにデータをダラ流しにするだけ、みたいなのならフレームワークなしで全然いける。

pathに応じたViewがいくつも、みたいなアプリならExpress使ったほうがいい。(plackとMojo::Liteの使い分けと似てる。)

(5) socket.io

ttps://github.com/LearnBoost/Socket.IO-node まんま。

// On the server:

var http = require('http'), 
        io = require('./path/to/socket.io'),

server = http.createServer(function(req, res){
    // your normal server code
    res.writeHead(200, {'Content-Type': 'text/html'});
    res.end('<h1>Hello world</h1>');
});

server.listen(80);

var socket = io.listen(server);

socket.on('connection', function(client){
  // new client is here!
  client.on('message', function(){})
  client.on('disconnect', function(){})
});

//On the client:
// <script src="/socket.io/socket.io.js"></script>

var socket = new io.Socket();
socket.connect();
socket.on('connect', function(){})
socket.on('message', function(){})
socket.on('disconnect', function(){})

データをサーバ・クライアント間でやりとりするメソッドは以下。

serverclient.send(message)Sends a message to the client.
serverclient.broadcast(message)Sends a message to all other clients.
clientsocket.send(data)

参考:ttp://d.hatena.ne.jp/Jxck/20100831/1283253824

2011-03-13

[][]node.js入門メモ(2)

http://dl.dropbox.com/u/219436/node.js/handson/build/html/index.html

非同期io

var util = require('util');
var url  = require('url');
var http = require('http');

function download(urlStr) {
    var u = url.parse(urlStr);
    var client = http.createClient(u.port || 80, u.hostname);
    var req = client.request('GET', u.pathname, { host: u.hostname });
    req.end();
    request.on('response', function(res){
        console.log(res.statusCode);
        for (var i in res.headers) {
            console.log(i + ": " + res.headers[i]);
        }
        res.setEncoding('utf8');
        res.on('data', function(chunk) {  // req has many data
            util.print(chunk);
        });
        res.on('end', function(){
            console.log*(;
        });
    })
}

download(process.argv[2]);

2つのプログラミングモデル

コールバック関数を渡すコールバックモデルの他に、「イベントを専用オブジェクトに定義し、関数の返り値として返す」イベント駆動モデルがある。

受け取る(発火させる)側

var EventEmitter = require('event').EventEmitter;
function download(url){
   var ev = new EventEmitter();

   request.on('response', function(response){
      // ... (snip) ...
      response.on('end', function(){
         ev.emit('notify', response, body);  // 発火
      });
   });

   return ev;
}

呼び出す側

var ev = download(url);
ev.on('notify', function(response, body){
   ...
});

メリット:

  • 異なるイベント名をつけることで,それぞれの状況に応じた引数を組み立てることができる
  • イベント受信側では複数のイベントハンドラを登録することができる

ライブラリを作る

メソッドをエクスポートする

exports.add = function() { .... };

node.jsでのクラス継承

var util = require('util');
function ClassB(){ .... }
// util.inheritsで継承
util.inherits(ClassB, ClassA);

[][]node.js入門メモ

node.jsとは何のためにあるものか

イベントループモデルと聞いて、最初はnginxとかと何が違うのか分からなかったけど、こういうことらしい。

apache はご存じのとおりスレッド、対して nginx はイベントループ。で、その性能の差は使用メモリ量などに端的に表れていたんだ。スレッドモデルの場合ね、実行スタックをコピーする必要があるからスレッドが増えるほどメモリ使用率が上昇する。対してイベントループモデルは1プロセスのままだからメモリはそれほど食わない。この点ではイベントループが有利っていうコンテクストだ。

ただね、イベントループモデルにも弱点がある。それはコードのどこかでブロックする処理が発生するとプロセス全体がストップしちゃう、つまりイベントループ自体の処理もストップしてしまい、パフォーマンスに大きく影響してしまうってことなんだ。

ryan はこの、イベントループにおけるブロックをなくそうと考えた。それが node.js のそもそもの始まりってわけ。

http://d.hatena.ne.jp/badatmath/20101020/1287587240

hello world

var sys = require('sys');
var http = require('http');

var server = http.createServer(
    function (request, response) {

        response.writeHead(200, {'Content-Type': 'text/plain'});
        response.write('Hello World!!\n');
        response.end();
    }
).listen(8124);

sys.log('Server running at http://127.0.0.1:8124/');

TCPサーバ

http://www.atmarkit.co.jp/fcoding/articles/websocket/01/websocket01b.html

var net = require('net');
var sys = require('sys');
var server = net.createServer(function (stream) {
  var array = [];
  stream.setEncoding('utf8');
  stream.on('connect', function () {
    stream.write('connected\r\n');
  });
  stream.on('data', function (data) {
    array.push(data.slice(0,-2));
    stream.write('res: ' + array.join(',') + '\n');
  });
  stream.on('end', function () {
    sys.log('disconnected')
    stream.end();
  });
});
server.listen(8127, 'localhost');

WebSocketを使う

node.jsとWebSocketは相性が良いらしい。

WebSocketはステートフル(一度クライアントとサーバとの間で接続が確立されると、その後のやり取りで状態を共有することができる)。必要なときにサーバサイドから情報を送ることができれば、ムダなトラフィックを減らせる。

var ws = new WebSocket("ws://example.com/service");
ws.onopen = function() {
  // Web Socket is connected. You can send data by send() method.
  ws.send("message to send"); ....
};
ws.onmessage = function (evt) { var received_msg = evt.data; ... };
ws.onclose = function() { // websocket is closed. };

naoyaさんのブログで、サーバ側をperl(Mojolicious::Liteに全部やってもらう感じ)、クライアント側をjs、っていうサンプルコードがあった。

http://d.hatena.ne.jp/naoya/20101011/1286778922

  var ws = new WebSocket('ws://localhost:3000/echo');
  ws.onopen = function () {
    log('Connection opened');
  };
  
  ws.onmessage = function (msg) {
    var res = JSON.parse(msg.data);
    log('[' + res.hms + '] ' + res.text); 
  };

  $('#msg').keydown(function (e) {
    if (e.keyCode == 13 && $('#msg').val()) {
        ws.send($('#msg').val());
        $('#msg').val('');
    }
  });

node.jsでWebアプリを作るチュートリアル

http://dl.dropbox.com/u/219436/node.js/handson/build/html/index.html