2012年3月29日木曜日

node.jsの紹介

こんにちは、Leeです。
初めての投稿です。よろしくお願いします。


最近、Flash又はUnity3Dで作ったMMORPGやHTML5で作ったFPSゲーム(Quake)等、ウェブブラウザでリアルタイムに動くアプリケーションを開発できる技術が発展していますので、
今日はリアルタイムのウェブアプリを作る為の技術の中でServer側の技術である、node.jsに対して話してみたいと思います。


< node.jsとは >
node.jsはgoogleのすごく速いインタープリターであるV8 javascript engineを基盤で作られたサーバーサイドのJavascriptです。Javascriptでゲームサーバー等を作成できると言う意味です。
node.jsはV8エンジンが持っている長所(速さ、高効率のガベージコレクション等)の上に、OSのようなevented, non-blocking I/Oを提供します。
Evented I/Oとは入出力処理で処理がブロックされないように入出力処理を全て非同期で扱うようになっていると言う意味で、Event Loopを利用して動作を要請する時Callbackを指定して動作が終わるとCallbackが実行される仕組みです。
node.jsではひとつのプロセスEvent Loopを利用して多くのHTTPコネクションを扱う動作モデルを取っているので、高トラフィック(=多数のユーザ)を効率良く扱えます。接続数が多ければ多いほど、1つのコネクション当りに別のThreadを実行する方式に比べて、メモリの節約や安定性や性能が高いです。

< node.jsのインストール >
node.jsとNPM(Nodejs Package Manager)の設置が必要です。
・Fedora
sudo yum localinstall --nogpgcheck http://nodejs.tchol.org/repocfg/fedora/nodejs-stable-release.noarch.rpm
sudo yum install nodejs-compat-symlinks npm
・CentOS
wget http://nodejs.tchol.org/repocfg/el/nodejs-stable-release.noarch.rpm
yum localinstall --nogpgcheck nodejs-stable-release.noarch.rpm
yum install nodejs-compat-symlinks npm
・Amazon Linux
sudo yum localinstall --nogpgcheck http://nodejs.tchol.org/repocfg/amzn1/nodejs-stable-release.noarch.rpm
sudo yum install nodejs-compat-symlinks npm
※Debian, Ubuntu, openSUSE等のOSでのインストールもリンクを参考してください。
  https://github.com/joyent/node/wiki/Installing-Node.js-via-package-manager
※Windows、OSXの場合はhttp://www.nodejs.org/でInstallerをダウンロードして設置する方が便利です。

・設置が完了できたら、shellにnodeやnpmを入力して確認して見ましょう。
・もしエラーが出たら、Path問題の可能性が高いので、
    > export NODE_PATH=/インストールPATH/node_modules:$NODE_PATH
 等でPATHを環境変数に追加してください。


< チュートリアル >
簡単なWeb ServerやTCP ServerのSampleを作成して見ます。

[Web Server]
#Server側
・hello_server.jsと言うファイルを作成します。
01.var http = require('http'); 02. 03.http.createServer(function(request, response) { 04.   response.writeHead(200, {'Content-Type': 'text/html'}); 05.   response.write('Hello, World~!!'); 06.   response.end(); 07.}).listen(1337); 08. 09.console.log('Server running at http://localhost:1337/');
     line03:web serverのObjectを生成。callbackをanonymous function形で指定。
    line04:response headerを伝送。
    line05:文字列’Hello, World~!!’を伝送。
    line07: listenerがrequestイベントが発生する時、callbackを実行。

・作成したserverを実行します。
>node hello_server.js
Server running at http://127.0.0.1:1337/
#Client側
・Web browserでserverに接続して見ます。












[TCP Server]
#Server側
・tcp.jsと言うファイルを作成します。
01.var tcp = require('net');
02.
03.var server = tcp.createServer();
04. 
05.server.on('connection', function(e) {
06.   e.write('hello!\n');
07.   e.end();});
08.
09.server.listen(8000);
    line03:tcp serverのObjectを生成。
    line05:'connection'イベントが発生すると、'hello!'を伝送。
    line09:serverのlistenerを実行。

・作成したserverを実行します。







#Client側
・telnetでserverに接続して見ます。







・'hello!'という文字列が出力されます。







以上です。Sampleが簡単すぎて、すみません。
普通はnodejsでサーバーを作成する時はnodejsのpackageである
socket.ioと言うものを使って作ります。
次回はWebSocketやsocket.ioに対して話して見たいと思います。


ありがとうございました。

2012年3月21日水曜日

第4回 HTML5のススメ

久々の更新 KOYAです。

さて、今回はHTML4までは出来なかった、マルチスレッドプログラミングをする方法をご紹介します。

わざわざjavascriptでマルチスレッドプログラミングをする理由ですが、
今までは高負荷な処理をjavascriptで行うとブラウザが固まったりしましたが、
HTML5から導入された機能を使えば、バックグラウンドで処理を行いブラウザが固まることなく
快適なネット生活を送ることが可能となりました。

ということで、あえて、待機状態のある状況を作ってみます。
以下がソースです。

 
<head>
    <script type="text/javascript">
        // テキストエリアに指定された秒数待つ。
        function sleep(){
            var sec = document.getElementById("num").value
            var start = new Date;
            while (1) {
                var cur = new Date;
                if (sec * 1000 <= cur.getTime() - start.getTime()) 
                {
                    break;
                }
            }
            alert(sec + "秒まったよ");
        }
    </script>
</head>
<body>
    秒数:<input type="text" id="num"><button onclick="sleep()">スリープ</button>
</body>
テキストエリアに指定された秒数分待機状態が入った後、alertで何秒待ったかを表示する単純なロジックです。
buttonを押すと指定秒数ブラウザが固まり、何もすることができなくなったと思います。
ということで今迄はやきもきしながら結果をまたなくてはなりませんでした。

しかし、HTML5から導入された機能『Web Workers』を使用すれば固まることがなく動作することが可能です。
以下がソースとなります。

main.html
 
<head>
    <script type="text/javascript">
        // インスタンスの生成
        var worker = new Worker("http://ほにゃらら/worker.js");

        // 結果返却後処理
        worker.onmessage = function(event) {
            alert(event.data + "秒まったよ");
        };
        
        // post送信
        function post() {
          worker.postMessage(document.getElementById("num").value);
        }
    </script>
</head>
<body>
    秒数:<input type="text" id="num"><button onclick="post()">スリープ</button>
</body>

worker.js
 
// メッセージの受信
onmessage = function(event) {
    sleep(event.data);
    postMessage(event.data);
}

function sleep(sec){
    var start = new Date;
    while (1) {
        var cur = new Date;
        if (sec * 1000 <= cur.getTime() - start.getTime()) 
        {
            break;
        }
    }
} 
ということで、バックグラウンドで処理を行うjsでWoekerオブジェクトを作成し、
postで指定されたjsにデータの送信を行い、jsの中で受け取った値をWokerオブジェクト
のonmessageイベントで取得し、alertで値を表示するというような処理を行なっています。
これでバックグランドで処理を行なっているので、ブラウザが固まることなく結果が帰って来たと思います。

HTML5が台頭したことにより、リッチな処理を行わなければならない事が増えてきたと思います。
そんな時には、是非WebWorkerを使用してブラウザが固まることを回避してみてください。

2012年3月15日木曜日

for文とforeach文の不思議な違い

こんにちは、matsuiです。

今回ご紹介する内容は、私が実際にPHPにてソーシャルアプリを実装していく際に挙動確認のために作ったforとforeachのサンプルソースです。

出だしから既に内容について推測出来てしまった方もいらっしゃるとは思いますが、
ここでは「なぜ今更forとforeachの動きを確認しているの?」と思って頂いたと仮定して話を進めさせていただきたいと思います。

forとforeachを使ったサンプルプログラムを3パターンほどご用意しました。
以下のソースをご覧ください。

// パターンA
$arrayList = array(
    0,
    1,
    2
);

foreach ($arrayList as $key => $value) {
    array_pop($arrayList);
    var_dump($arrayList);
}

// パターンB
$arrayList = array(
    0,
    1,
    2
);

$length = count($arrayList);
for ($i = 0;$i < $length;$i++) {
    array_pop($arrayList);
    var_dump($arrayList);
}

// パターンC
$arrayList = array(
    0,
    1,
    2
);

for ($i = 0;$i < count($arrayList);$i++) {
    array_pop($arrayList);
    var_dump($arrayList);
}

それぞれの違いについてお分かりでしょうか?
配列の宣言は全て同じで、array_pop関数を使って宣言した配列の最後の要素を取り出した後、配列の中身をvar_dumpを使って出力しています。
違いはforかforeachで処理しているかと配列の長さをどこで取得しているかの違いです。
各パターンの出力結果の違いについて想像してみてください。

出力結果は以下。

パターンAの結果
array(2) {
[0]=>
int(0)
[1]=>
int(1)
}

array(1) {
[0]=>
int(0)
}

array(0) {
}

パターンBの結果
array(2) {
[0]=>
int(0)
[1]=>
int(1)
}

array(1) {
[0]=>
int(0)
}

array(0) {
}

パターンCの結果
array(2) {
[0]=>
int(0)
[1]=>
int(1)
}

array(1) {
[0]=>
int(0)
}

出力結果は想像通りだったでしょうか?
結果だけ見るのであればパターンAとパターンBは同じです。
パターンCだけが結果が違うのは、配列の要素数をどのタイミングで取得し、終了条件と比較しているかという点が他と違うためです。

さて、そろそろ本題に入ります。
以下のような2つのプログラム、パターンDとパターンEがあります。

// パターンD
$arrayList = array(
    0,
    1,
    2
);

foreach ($arrayList as $key => $value) {
    $popValue = array_pop($arrayList);
    array_unshift($arrayList, $popValue);
    var_dump('key : ' . $key . " value :" . $value);
    var_dump($arrayList);
}

// パターンE
$arrayList = array(
    0,
    1,
    2
);

$length = count($arrayList);
for ($i = 0;$i < $length;$i++) {
    $popValue = array_pop($arrayList);
    array_unshift($arrayList, $popValue);
    var_dump('key : ' . $i . " value :" . $arrayList[$i]);
    var_dump($arrayList);
}

今までの例と同じように配列の宣言は一緒です。
違うのはforとforeachの構文とそれに伴う配列の要素に対するアクセス方法の違いだけで、「配列の最後の要素を取得し、素配列の最初の要素として登録する」という動き自体は一緒です。
どんな実行結果が出るか想像してみてください。

実行結果は以下です。

パターンD
string(16) "key : 0 value :0"
array(3) {
[0]=>
int(2)
[1]=>
int(0)
[2]=>
int(1)
}

string(16) "key : 1 value :1"
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(0)
}

string(16) "key : 2 value :2"
array(3) {
[0]=>
int(0)
[1]=>
int(1)
[2]=>
int(2)
}

パターンE
string(16) "key : 0 value :2"
array(3) {
[0]=>
int(2)
[1]=>
int(0)
[2]=>
int(1)
}

string(16) "key : 1 value :2"
array(3) {
[0]=>
int(1)
[1]=>
int(2)
[2]=>
int(0)
}

string(16) "key : 2 value :2"
array(3) {
[0]=>
int(0)
[1]=>
int(1)
[2]=>
int(2)
}

実行前に想像した結果と実際の実行結果が違った方も、いらっしゃるのではないかと思います。
パターンDは、実際の宣言した配列の出力内容とforeach構文によって取得した要素($keyと$value)の中身が違っています。

どうしてこのような結果の違いが生まれるのでしょうか。

PHPのソースコードを読めば明確な答えが出るのでしょうが、ちょっと時間的にそうも言ってられないので、
プログラミングPHP(第2版)という本からその原因と思われる個所についての説明を拝借してくることにします。

foreachは配列自体を操作するわけではなく、配列のコピーを作成して処理します。foreachループ内で配列の要素を削除したり挿入したりしても、ループではその要素を反映しません。

だそうです。
配列の確かにコピーを作成して処理した場合の動きだと考えると、動作結果に納得がいきます。

特に意識をして使い分けをするようなケースは余りないかもしれませんが、forとforeachには明確な違いがあるということが実験から明らかになりました。

残念ながら内部動作まで私自身が読み解いたわけではありませんので、「違いがある」という結論以上のことを明言できないのが残念ですが、今回はここまでとさせてください。