こんにちは。しゅうです。
今日は巷で話題のnode.jsを触ってみました。
node.jsは簡単に言うとサーバーサイドスクリプトとしてjavascriptを使用するためのものです。
サーバーサイドスクリプトと言えば代表的なものとしてPHP,PERL,Pythonを挙げることができますが
node.jsを使用するとそれらスクリプト言語と同様の処理をjavascriptで実装できるようになります。
ちなみに実体としてnode.jsというjavascriptが存在するわけではなく、サーバサイド処理を実装するためのライブラリを指します。
javascriptエンジン自体はGoogle Chromeと同じV8が使用されています。
さて、、、、
これを使うと何のメリットがあるの?って思うと思います。
私はPHP使いですが、正直私もjavascriptでwebアプリケーション書いて何の得があるの?って思いました。
なので他のnode.jsの紹介記事等を少し調べてメリットをまとめてみました。
- みんな大好き(?)なjavascriptでサーバーサイド処理も実装できる。
- webアプリケーションを開発した場合、LAMP環境で開発した場合よりパフォーマンスが良い
1は。。。正直別にjavascriptが好きなわけではないのでどうでもいいのですが
2に関してとても興味がわきました。パフォーマンスを求めるならJAVA使えよって思うかもしれませんが
開発速度を担保できないので個人的にはPHPで開発するのがベストと思っているものの、アクセス数が
多い場合にはパフォーマンスが著しく低下するため、もし開発がPHPと同程度の速度で可能であり
かつパフォーマンスも良いと言われると惹かれるものがあります。
LAMP環境よりパフォーマンスが良いと言われる理由はapacheとnode.jsの並列処理の仕組みの違いが
あるためですが、今回は割愛します。
。。。。ということでとりあえずLAMP環境と対決させることにしてみます。
今回はリアルサーバーは使用せず、WindowのVMPlayer上で稼働するCentOS5上で試すことにします。
まずnode.jsをインストール!!
(ブログ執筆時点の最新バージョンは0.4.8です)
wget http://nodejs.org/dist/node-v0.4.8.tar.gz
tar zxvf node-v0.4.8.tar.gz
cd node-v0.4.8
次にインストール!!
./configure
make
make install
インストールされているかを確認します。
node -v
上記の結果として「v0.4.8」が表示されればインストール完了です。
これでnode.jsを使用する準備が整いました。
それでは試にお馴染みのブラウザ上に「hello world!!」を表示するアプリを作ってみましょう。
・・・と思いましがhello world!!ではなく「こんにちは!!」にします。
var http = require('http');
http.createServer(function(req , res){
var data = "はろー";
res.writeHead(200 , {'Content-Type:' : 'text/html; charset=utf8'});
res.end(data , "utf8");
}).listen(1338,'192.168.0.170');
console.log('node js web server is running....');
上記内容をviewtest.jsの名前で適当な場所に保存します。
そして以下のコマンドをコマンドライン上から実行します。
node viewtest.js
上記を実行すると
「node js web server is running…」というメッセージが表示されます。
これでport1338でhttpリクエストを待ち受ける準備が完了しました。
ではブラウザ上からhttp://192.168.0.170:1338/にアクセスしてみます。
ブラウザ上には「こんにちは!!」と表示されます。
キャプチャーは割愛させていただきますが実際に試していただくと表示されると思います。
それでは次にサードパーティー製ですがnode.js用mysqlライブラリをインストールします。
LAMP環境との対決ですからmysqlへのアクセスは必須です。
インストールと言ってもjavascriptで記述されているため、配置するだけです。
まず最初に適当な場所にnode.jsサードパーティー製ライブラリ用ディレクトリを作成します。
今回はnodelibという名前で作成します。
そしてカレントディレクトリをnodelib直下に変更します。
mkdir nodelib
cd nodelib
このライブラリはgitを使用して管理されているためgitコマンドにより次のように取得します。
git clone git://github.com/felixge/node-mysql.git mysql
これでnodelib内にmysqlというフォルダーでnode.js用mysqlライブラリがインストールされます。
次にデータベースの作成、テーブルの作成を行います。
mysqlにログインしてデータベース「nodejstest」の作成、テーブル「test1」の作成を行います。
mysql -u***** -p
Enter password:*******
mysql>create database nodejstest charset utf8;
mysql>use nodejstest
mysql>create table test1(
> id int not null auto_increment,
>data text default "",
>primary key(id)
>) Engine=MyISAM;
そしてテスト用データ10000万件を入れます。
dataは「あああああああああ。。。。。」とします。idはauto_incrementですので自動的に
採番・挿入されます。
テスト用データベース、テーブルの作成が完了したら次はリクエストパラメータ「id」を取得し
取得したidをキーとしてテーブルtest1を参照し、ブラウザ上にidと内容を出力するwebアプリケーション
をnode.js上で実行させるためにjavascriptで作成します。
require.paths.unshift('(インストールしたサードパーティー製mysqlライブラリルートへの絶対パス)');
var http = require('http');
var Client = require('mysql').Client;
var client = new Client();
var DATABASE_NAME = "nodejstest";
var TABLE_NAME = "test1";
/* 接続情報設定 */
client.user = '******';
client.password = '**********';
client.host = 'localhost';
client.port = 3306;
client.database = DATABASE_NAME;
http.createServer(function (req ,res) {
/* mysqlサーバー接続 */
client.connect();
/* 文字コード設定 */
client.query('set character set utf8');
/* 使用DB選択 */
client.query('use ' + DATABASE_NAME);
/* 条件 */
var urlParts = require('url').parse(req.url);
var where = " id = " + urlParts.query.split('=')[1];
/* データ取得・表示 */
var query = client.query('select * from ' + TABLE_NAME + " where " + where);
client.end();
query.on('row' , function(row) {
res.writeHead(200 , {'Content-Type' : 'text/html charset=utf8'});
res.end(row.id + ':' + row.data , "utf8");
});
}).listen(1338 , '192.168.0.170');
console.log('nodejs web server is running.....');
上記を「mysqltest.js」の名前で適当な場所に保存します。
では動作確認をします。次のコマンドをコマンドラインから入力後
node mysqltest.js
ブラウザからhttp://192.168.0.170:1338/?id=1にアクセスしてみます。
「1:ああああああああああああああああああああああああああああああ」
と表示されます。
では次にphpで同じ処理を行うwebアプリケーションを作成します。
極力phpのネイティブ関数・クラス以外の使用を避けます。
<?
$user = "******";
$pass = "*******";
$host = "localhost";
$database = "nodejstest";
/* 接続 */
if (!($con = mysql_connect($host , $user , $pass))) die('connect failed.');
/* DB選択 */
if (!mysql_select_db($database , $con)) die('select db failed');
/* クライアンエンコーディング設定 */
mysql_query("set character set utf8");
/* 条件 */
$where = " id = " . (isset($_GET['id']) ? $_GET['id'] : "");
/* 取得 */
$sql = "select * from test1 where " . $where;
if ($ret = mysql_query($sql , $con))
{
if ($rec = mysql_fetch_assoc($ret))
echo $rec['id'] . ":" . $rec['data'];
}
else die('failed query');
mysql_close($con);
?>
上記内容をindex.phpでドキュメントルートに保存します。
(apach関連の設定は割愛します。待ち受けポートは1337に設定してあります。)
では動作確認をしてみます。
ブラウザからhttp://192.168.0.170:1337/index.php?id=1にアクセスします。
「1:ああああああああああああああああああああああああああああああ」
が表示されます。
これでLAMP環境とnode.jsを対決させる準備がようやく整いました。
jmeterを使用し、javascriptで作成されたwebアプリケーション、phpで作成されたwebアプリケーションそれぞれに
同時接続数100、リクエストパラメータid=1で負荷をかけます。
その結果が以下です。結果は「統計レポート」として出力しました。
node.js上で実行させたwebアプリケーション
Average:3
Median:3
90% Line:4
Min:3
Max:6
Error(%):0
Throughput:10.1/sec
LAMP環境で実行させたwebアプリケーション
Average:4
Median:4
90% Line:5
Min:3
Max:7
Error(%):0
Throughput:10.1/sec
・・・・正直なところあまり変わりません。
では今度は同時接続数を400まで増やしてみましょう。
その結果が以下になります。
node.js上で実行させたwebアプリケーション
Average:4
Median:3
90% Line:4
Min:2
Max:207
Error(%):0
throughput:39.8/sec
LAMP環境上で実行させたwebアプリケーション
Average:3
Median:3
90% Line:4
Min:2
Max:15
Error(%):0
throughput:39.7/sec
・・・・あれれ!?また変わんない?・・・・
というよりもLAMP環境上で実行させたwebアプリケーションの方が
総合的に見て若干まし?
個人的にはnode.js圧勝!!を期待していたのですが・・・
最後に同時接続数500で実行してみます。
・・・・・果たして結果は・・・・?
結果、node.js上で実行されていたwebアプリケーションは死亡してしまいました。
出力されたエラーは以下の通りです。
events.js:45
throw arguments[1]; // Unhandled 'error' event
^
Error: Got packets out of order
at Function._packetToUserObject (/root/nodelib/mysql/lib/mysql/client.js:342:7)
at Client._handlePacket (/root/nodelib/mysql/lib/mysql/client.js:304:21)
at Parser.<anonymous> (/root/nodelib/mysql/lib/mysql/client.js:83:14)
at Parser.emit (events.js:64:17)
at /root/nodelib/mysql/lib/mysql/parser.js:75:14
at Parser.write (/root/nodelib/mysql/lib/mysql/parser.js:580:7)
at Socket.<anonymous> (/root/nodelib/mysql/lib/mysql/client.js:63:16)
at Socket.emit (events.js:64:17)
at Socket._onReadable (net.js:678:14)
at IOWatcher.onReadable [as callback] (net.js:177:10)
もう少し詳しく書くと、2リクエスト処理した時点で死亡しました。
正直なところ具体的な理由はわかりませんが、取得したパケットが多すぎる?みたいなエラーで落ちてるようです。
(詳細分かる人求む)
これはこれは残念・・・・まだ実用に耐える程の品質はないみたいです。
実はmysqlにアクセスせず(単純に固定文字列をブラウザに出力するだけ)
かつ同時接続数が多い場合(同時接続数1000でテストしました)にはnode.jsが圧勝でした。
実際webアプリケーションを開発する場合、DBへのアクセスは必須になりますので
現状、node.jsは実用的なレベルに達してない・・・?と言わざるを得ないかもしれません。
別の方で同じような試みをした方がいらっしゃり、かつ私の結果と正反対だったという方が
いらっしゃったら一言いただけたら幸いです。
では今日はこの辺で・・・・
shu Javascript