普段何気なく使っている技術や言葉について、表面的な知識にせず、しっかりとイメージできるまで自分なりに調べて整理し、理解するシリーズ。
目次
Node.jsとは
Node.jsはサーバサイドで動くJavaScript実行環境で、軽量で効率よく多くのリクエストを処理するネットワークアプリケーションの構築が可能です。
Node.jsの特徴
JavaScriptでサーバサイドの処理を実装できる
JavaScriptによってサーバサイドの処理を実装できることで、フロントエンドからサーバサイドまでをJavaScriptのみで記述することができます。
これまでサーバサイドに手を出せなかった人が、フロントからサーバサイドまで通貫して行えるようになるというのは、大きな特徴であり利点になります。
ノンブロッキングI/O
ノンブロッキングI/Oによって、シングルスレッドで複数の処理を非同期に行うことができます。例えばDBにアクセスしたら、呼び出し元はその結果を待たずに次の処理に移り、DBの結果が出たというコールバックが返ってきたら、その結果を受け取ります。シングルスレッドなので、アクセスが増えてもメモリの消費量は少なく済みます。
このようにノンブロッキングI/Oによって、少ないメモリ消費で多くの処理を行うことができます。
ちなみに対義語としてブロッキングI/Oがあり、こちらはノンブロッキングI/Oとは違い呼び出し元はその結果を待ってから次の処理に進みます。
ノンブロッキングI/OとブロッキングI/Oのわかりやすい説明は以下が参考になります。
シングルスレッド
シングルスレッドとマルチスレッドについて整理しておきます。
Apachなど一般的なサーバーは「マルチスレッド」に対応しており、複数のアクセスが来た場合にはそれぞれメモリを割り当てて処理を行います。しかしこの方法は「C10K問題(詳しくはhttps://ja.wikipedia.org/wiki/C10K%E5%95%8F%E9%A1%8C)」と言われるように、1万規模の大量アクセスが来た場合にメモリがパンクして効率が悪くなります。
「シングルスレッド」の場合は、1つのメモリでアクセスを1つずつ処理するのでメモリ効率は良いが、1つずつしか処理できないので大量のアクセスを制御しにくくなります。
その点Node.jsは、JavaScriptの特徴であるシングルスレッド基盤の言語であり、非同期処理ができるため、少ないメモリ消費で大量のアクセスを高速に処理が可能になります。
イベントループ
シングルスレッドはスレッドが1つなのに多くの作業が同時に処理できるのは、同時実行をサポートしているイベントループという仕組みがあるからです。
イベントループは、リクエストやコールバックなどをイベントとして扱い、そのイベントに関する処理が終わった後に次のイベントを処理するように動作します。
イベントループの対義語はスレッドになります。スレッドはリクエストに対し、スレッドを立ち上げて対応します。リクエストが大量にくると、処理待ちが発生します。
例えば、Apacheはスレッドモデル、Nginxはイベントループモデルで動作します。スレッドモデルの場合はスレッドが増えるほどメモリの使用率が上昇します。イベントループモデルではシングルスレッドなので、メモリの使用率は少なく済みます。
イベントループモデルにもデメリットがあり、イベントが終わった後に次のイベント、またその次のイベントというように進むため、どこかで処理がブロックされると全体のイベントが止まってしまいます。
そこでNode.jsでは、このイベントループに加えてノンブロッキングI/Oがあることで、少ないメモリ消費で複数の処理を非同期に行うことができます。
「V8」エンジンによる高速な実行環境
Node.jsはJavaScriptを高速に実行させるため、Googleが開発した実行エンジン「V8」を採用しています。「V8」はChromeブラウザに搭載されており、これをサーバーサイドのNode.jsでも使えるようにしています。
従来の実行エンジンと違い、JavaScriptを即座にコンピュータが理解できる機械語に変換して処理を行うため非常に高速です。
このような実行エンジンを利用することで、Node.jsのさらなる高速な処理の実現を可能にしています。
Node.jsでHello Worldをやってみる
まずは基本のHello Worldを試してみます。
1 2 3 4 5 6 7 8 9 10 | var http = require('http'); var server = http.createServer(function(req, res){ res.writeHead(200, {'ContentType': 'text/plain'}); res.write('Hello World'); res.end(); }); server.listen(1234); console.log('サーバーを起動しました'); |
一つずつ処理をみていきます。
まずrequire関数でhttpオブジェクトを生成しています。
次に、httpオブジェクトのcreateServerメソッドでサーバーオブジェクトを生成しています。これがnode.jsでつくるサーバーとなります。
コールバック関数の引数にhttpリクエストオブジェクト(第一引数)、httpレスポンスオブジェクト(第二引数)を受け取っています。
サーバーへリクエストが送られた際の動作を定義しています。
- writeHeadメソッドでhttpヘッダを書き出す(デフォルトは200, {‘ContentType’: ‘text/plain’})
- writeメソッドでhtmlコンテンツを書き出す
- endメソッドでhtmlコンテンツの出力を完了する
生成したサーバーオブジェクトをlistenメソッドで待ち受け状態にします。引数ではポート番号を指定しています。
ExpressでHello Worldをやってみる
ExpressはNode.jsで利用できるWebアプリケーションフレームワークで、広く使われているフレームワークの1つですです。
npmを初期化します。以下のコマンドを実行したらカレントディレクトリにpackage.jsonが生成されていることを確認しましょう。
1 | $ npm init |
Expressをインストールするには、npmを使用します。以下のコマンドでインストールできます。
saveオプションを使うとインストール対象のバージョン情報がpackage.json記録されます。
1 | $ npm install express --save |
ExpressでHello Worldを試してみます。
1 2 3 4 5 6 7 8 9 10 | var express = require('express'); var app = express(); app.get('/', function(req, res){ res.send('Hello World'); }); var server = app.listen(1234, function(){ console.log('サーバーを起動しました'); }); |
一つずつ処理をみていきます。
まずrequire関数でExpressのオブジェクトを生成しています。
Expressオブジェクトのexpressメソッドで「アプリケーション」としての役割を担うオブジェクトを生成します。
次にアプリケーションオブジェクトのgetメソッドでGETリクエストの登録します。
この例ではルートにアクセスされた際の処理のみを登録していますが、リクエストの登録は必要なだけ何回でも行えます。sendメソッドは、様々なタイプの応答を送信するためのメソッドです。
最後にアプリケーションオブジェクトのlistenメソッドでポート番号とコールバック関数を指定して待受状態にしています。
Expressプロジェクトの雛形を作成
Expressアプリケーションの雛形は「expressgenerator」と言うライブラリを使用して自動生成します。
「-g」はグローバルインストールを行うためのオプションで、ライブラリをNode.jsのグローバルな領域へインストールします。expressgeneratorはプログラム中で利用するものではないため、グローバルな領域にインストールします。
1 | $ npm install express-generator -g |
雛形の作成は以下のexpressコマンドで生成します。
1 | $ express --view=ejs |
ここで使用している「–view=ejs」と言うオプションは、「テンプレートエンジンはEJSを使う」という意味です。このオプションを指定しないと「jade」と言うテンプレートエンジンを使う設定になってしまいます。
このコマンドを実行すると実行したディレクトリ内にいくつかのフォルダやファイルが生成されます。
雛形を生成したら、そのままディレクトリを移動せず(D:\node\expgeneのまま)次のコマンドを実行します。npmはpackage.jsonの「dependencies」に指定されているライブラリを一括でインストールする機能を持っています。
1 | $ npm install |
以下のコマンドでサーバーが起動します。デフォルトで用意されているindex.jsにアクセスします。
1 | $ npm start |
Express雛形のリソースの説明
binディレクトリ
binはサーバを起動するためのwwwファイルを格納しているディレクトリです。
通常、サーバを起動すること以外はあまり意識する必要はありません。このwwwファイルにはapp.jsを呼び出してサーバを実行するためのスクリプトが記述されています。
publicディレクトリ
画像やCSS、JSライブラリなどの静的ファイルを配置するためのディレクトリです。
images、stylesheets、javascriptsの3つのサブディレクトリも用意されています。
routesディレクトリ
画面ごとの処理を記述したJSファイルを配置するためのディレクトリです。デフォルトでindex.jsとusers.jsが配置されています。このディレクトリ配下に各画面用のJSファイルを用意して処理を実装します。
viewsディレクトリ
画面のテンプレートファイルを配置するためのディレクトリです。
テンプレートエンジンにEJSを指定した場合、デフォルトでindex.ejsとerror.ejsが配置されます。
app.js
プログラムのメインとなるファイルで、wwwから呼び出される前提の内容になっています。このファイルには最初から主に次の処理が実装されています。
- 各種オブジェクトの生成
- オブジェクトの利用宣言
- リクエスト毎の遷移設定
- エラー時の処理
これらのファイルには各種「宣言」に関する処理を実装します。処理はroutes配下に用意する画面ごとのJSファイルに実装するからです。
開発を進めるにあたり、画面を増やしたり新たに利用するライブラリを増やしたりする場合、このファイルにはそれらの「宣言」を加えていくことになります。
参考
Node.jsとは?特徴や活用する利点を初心者向けにわかりやすく解説 | 侍エンジニアブログ