Edit
NODE.jsとは

Edit
インストールの方法

Edit
yumからのインストール

yumからのインストールを行うと場合によっては、yumがおかしくなるため現状は他の方法が望ましい。これは、node.js がyumリポジトリから削除されたため、一度、yumでインストールすると、yum がnode.jsの情報をyumリポジトリに見にいくのだが、データが無いといって停止する。

Edit
ソースからのインストール

OpenSSLのバージョンが古いとインストールに失敗することがある。

# wget http://nodejs.org/dist/v0.12.2/node-v0.12.2.tar.gz
# tar zxvf node-v0.12.2.tar.gz
# cd node-v0.12.2                              <- 展開名は自分で確認
# ./configure --prefix=/usr/local/node         <- 必要であれば、prefix を指定
# make
# make install

※gyp_node でエラーがでる場合は、このページのパッチを行うとコンパイルは通るようになる。

Edit
OSXでのインストール

nvm(Node Version Manager)を使ってインストール

# git clone git://github.com/creationix/nvm.git ~/nvm
# . ~/nvm/nvm.sh
# nvm install v0.6.14

Edit
CURL or WGET でのインストール(推奨)

最初にnvmをインストールしてからnode.jsをインストールする。nvm(Node.js Version Manager)するには、下記スクリプトで行う。

# curl -o- https://raw.githubusercontent.com/creationix/nvm/master/install.sh | bash

or wget

# wget -qO- https://raw.githubusercontent.com/creationix/nvm/v0.33.11/install.sh | bash

これを行ったあと、環境設定ファイルに読み込まれるので、再ログインするか環境設定ファイルの読み直しを行わないと、コマンドは使えない。

# nvm ls-remote

このコマンドでインストールしたい node.js のバージョンを確認する。

# nvm install stable

これで最新の安定版がインストールされる。

Edit
Node.js のバージョンを変更したい場合

切り替えたいバージョンをインストールして、use で切り替える。

# nvm install v8.12.0
# nvm use  v8.12.0

Edit
nodeno

基本バージョンを指定

# nvm alias default v10.11.0

Edit
npmの代わりに yarn を入れる

# npm install -g yarn

Edit
インストール確認

ノード自体のバージョン確認

# node -v

ノードで利用されている V8 エンジンのバージョン確認

# node -e 'console.log(process.versions.v8)'

Edit
テスト

# git clone git://github.com/ry/node_chat.git
# cd node_chat
# node server.js

Edit
node-inspectorによるデバッグ

このツールは、サーバーで動くプログラムを、手元のブラウザを使ってステップ実行させることのできる遠隔デバッガーという認識が一番わかりやすいだろう。 参考

Edit
socket.io

Edit
socket.ioの概要

socket.ioは、主にサーバと、クライアントとの間にsocket(トンネルのイメージ)で繋げて、双方でデータを簡単にやり取りできる。(要はサーバーから、非同期にクライアントにデータが送れる。通常のWEBアプリでは、ページの更新タイミングでないとサーバからデータが取得できない)

Edit
Install

# npm install socket.io

必要に応じて

# npm install -g websocket.io
# npm install mysql

Edit
応用例

Edit
namespaceの設定によるポートの多機能化

ポートIO(socket.io本体)の定義に、「.of('/name')」という行を複数設定することで、コネクションを振り分ける事ができるので、サーバープログラムを再起動し直すことなく、サーバー側の機能を増やしていく事ができる。(例:.of('/login') や、.of('/shopping')など画面毎や機能毎にわける)

’’SERVER''

var io = require('socket.io').listen(80);
// chat画面用
var chat = io
  .of('/chat')
  .on('connection', function (socket) {
      socket.emit('a message', {  that: 'only'  , '/chat': 'will get' });
      chat.emit('a message', { everyone: 'in' , '/chat': 'will get' });
  });
// ニュース画面用
var news = io
  .of('/news')
  .on('connection', function (socket) {
      socket.emit('item', { news: 'item' });
  });

CLIENT

 <script src="http://localhost/socket.io/socket.io.js"></script>
 var chat = io.connect('http://localhost/chat')
   , news = io.connect('http://localhost/news');
 
 chat.on('connect', function () {
   chat.emit('hi!');
 });
 
 news.on('news', function () {
   news.emit('woot');
 });

Edit
セッションIDを取得してクライアントを特定しメッセージを送る。

セッションIDは、connection が張られた時に、クライアント毎にユニークな番号を自動的に作成することで、クライアントの識別が行える。

// セッションIDの人にのみメッセージを送るサンプル。
var io = require('socket.io').listen(80);
io.sockets.on("connection", function (socket) {
   socket.on("message", function (data) {
       io.sockets.emit("message", {value: data.value});  // 全員にメッセージ
       io.sockets.socket(socket.id).emit('message', { value: data.value });  // 送った人のみ送信。
   });
});

Edit
チャネルを利用したグループ分けメッセージング

ver 0.7.x以降のsocket.ioでは、チャネル機能が搭載された。基本的にはnamespaceを使う事で、同様の機能は提供されるが、namespaceではコーディング時にグループが決定されている必要があり、ダイナミックなグルーピングには、チャネルが向いている。

Edit
socket通信の方式を切り替える。

io.set( 'transports', [
   'flashsocket',
   'websocket',
   'htmlfile',
   'xhr-polling',
   'jsonp-polling'
]);

Edit
トラブルシューティング

Edit
socket.io で引数にオブジェクトが使えない。

これは、下記のようなメッセージ用のdataオブジェクト変数を作る際に、

io.emit(message, data);

このdataを、初期処理で配列型にすると、後からの代入でもその後も配列型にされてしまい、データ送信ができなくなる。(一見すると同じに見える)

<送信出来ない例>

var data = [];
data.param1 = "test";
data.param2 = "param2 etc";

<送信できる例>

var data = {};
data.param1 = "test";
data.param2 = "param2 etc";

Edit
WebSocket

Socket.io は、ブラウザとの通信に利用する場合などには、非常に便利ではあるが、スマホなどの組込みアプリから通信するには、socket.io 互換のライブラリが必要となる。(これは、socket.io で websocket を利用する設定しても、通常の websocket との親和性が低く、あまり使えない。)そのため、websocket ライブラリは大抵のスマホで、ライブラリがあるので、websocket ライブラリを使うのも良い。現在、node.js で websocket のライブラリは、2つあるが速度も含めてあまり大差がないので、このみで選んでも良い。(若干の機能の違いがあるが、一般的には考慮の必要はないだろう)

インストールは基本的に以下の形でよい。

# npm install ws

※ npm に、-g オプションをつけると正しく起動しない場合がある。

Edit
運用

JavaのTomcatなどのアプリ管理を統括した、サーバープログラムと違い、nodeは只のインタプリタにしかすぎない。よって、アプリにバグや問題が合った時には、nodeごとプロセスが落ち、他のユーザーにも影響は発生する。またアプリ1ソケットという考え方のnodeでは、違うソケットを利用する、違うアプリは別々に起動するような概念が近い。逆説的には1つのアプリだけを起動する場合に利用するべきと考える。しかし、落ちたまま放置では問題がある場合が多く、その場合は、deamontools というプロセス管理ソフトと組み合わせると良い。

Edit
自動起動

forever を利用し、node がバックグランドで起動し、落ちた場合も再起動されるようにする。

# npm install forever -g --unsafe-perm
# forever server.js

Edit
更に良い自動起動

# npm install pm2 -g
# pm2 start server.js

pm2コマンド詳細

Edit
停止

<全停止>

pm2 stop all

<リロード>

pm2 reload all 

Edit
リストから削除

# pm2 delete 0           <- 最後の数字は削除するタスク ID

Edit
起動タスクの確認

# pm2 list

Edit
環境整備

基本的に、deamontools のインストールは、deamontoolsのページで確認してもらうとして、ここではnodeとの組み合わせ時の設定を説明したい。

Edit
deamontoolsへのサービスとして登録

deamontoolsをインストールし、プロセスが起動していることを確認後、自動起動させるサービスを登録するために、以下のコマンドを入力する。

# cd /service
# mkdir node;chmod +t ./node;mkdir ./node/log

Edit
リファレンス 1.0以降

Edit
環境変数に関して

Edit
node.js固有の環境変数としてよく使う単語

NODE_PATH ... node modules がインストールされているパスの設定(通常設定不必要)
NODE_ENV ..... リリース版(production)、デバッグ版(development)などの引数を渡したい時に主に利用。

Edit
他の環境変数を参照したい場合。

環境変数の'PATH'を取得する方法。

var env = process.env;
var path = env.PATH;

Edit
socket.io

通信に利用するライブラリ

Edit
set & get が無くなったので優先プロトコル設定は下記の通り。

var io = require('socket.io')( 8080, { transport: 'flashsocket,websocket,htmlfile,xhr-polling,jsonp-polling' });

代わりにmiddlewareという概念が導入された。これは、ハンドウェイクから接続までの間に処理を入れることが出来る。 (0.9 の時は、set("authorization") で、似たような専用機能だったものを 1.0 では middlewareとして汎用化した) また middleware はnamespace単位。

下記は同じ意味(デフォルトネーム)また、すべてのクライアントは必ずデフォルトネームに最初に接続。

io.use( function( socket, next){} );
io.of('/').use( function( socket, next){} );

fooネームスペース

io.of('/foo').use( function( socket, next){} );

fooネーム空間へのアクセス方法(クライアント) var socket = io('http://localhost:8080/foo');

Edit
バイナリサポート

サイズが大きいものは不向き(データを一旦メモリに読み込むため)但し、前段にデータ分割などを入れれば可能かも。 <オブジェクトにバイナリの埋め込み>

socket.emit( 'bdata', { data: new ArrayBuffer( 10 )} );

<複数バイナリ>

socket.emit( 'bdata', [ binary1, binary2 ] );

Edit
transportの種類

flashsocket がサポートされなくなった。

var io = require( 'socket.io' ).listen(server, {transports:[ 'polling','websocket' ]});

再接続時にupgradeを再度行わない為のモード

rememberUpgrade: false

Edit
middleware中に、ハンドシェイクデータにアクセス可能

よってクッキーやセッションの復元が出来るように。

io.use( function( socket, next ){
  console.log( socket.request );
  next();
});

Edit
複数のサーバーとの情報交換用に、adapterが実装。

実態は只のブロードキャスト <実際にはredisアダプタが利用される>

var redis = require( 'socket.io-redis' );
io.adapter( redis( {host: 'localhost', port: 6379} ));

Edit
デバッグ出力しなくなった。

1.0 では標準でデバッグ出力しなくなった。よってアプリ起動時に出力レベルを決める。この場合*なので、全モジュール出力。

# DEBUG=* node myapp.js

Edit
connect時のURL指定にはプロトコル識別子が必要

httpなどのプロトコルを記入しないと、file:// になってしまう。
<クライアント>

socket = io.connect( "http://my-server.com/", options );

Edit
how-to

Edit
オブジェクトの中身をすべて表示させる。

var util = require('util');
console.log( util.inspect( display_obj ) );

Edit
eclipseでサーバーのnode.jsをデバッグ

Edit
EclipseにGoogle Chrome Developer Pluginsをインストール。

Eclipseの plugin install メニューから、以下のURLを入力しインストール。

http://chromedevtools.googlecode.com/svn/update/dev/.

Edit
プロジェクトの作成

Eclipseのメニュー、[File]-[New]-[Project]-[JavaScript]-[JavaScript Project]でプロジェクトの作成。

Edit
nodeをdebugモードで起動

# node --debug-brk=5858 test.js           <- 5858 はポート番号

Edit
Eclipse で debug 環境の設定

eclipseのメニューから、[run] -> [debug configuration] でIPとPORTをしていして、debug ボタンで開始。

Edit
socket.io 1.0からの、socket.request の中身

io.engine.clientsCount : クライアントの接続数
socket.request.cooki : cookie データの取得

Edit
個別socket.id にメッセージを送る方法が変更された。

<0.9以前>

io.sockets.socket(socket.id).emit( 'message', "message to client" );

<1.0以降>

io.to( socket.id ).emit( 'message', "message to client" );

Edit
Uncaught TypeError: Cannot read property 'tagName' of null

という正体不明のエラーがでる。どうやら node.js を利用してindex.htmlなどを提供したあと、socket.io に接続すると、chromeでのみ発生する正体不明のバグ。firefoxでは発生しない。特にページにボタンが置いてあり、その上にマウスカーソルを移動させるだけで、このエラーのログがバンバン吐出される。(どうも、クロスサイトの認識あたりがあやしいような気配)

Edit
socket.ioとhttpポートを同じにする場合の注意

通常の webocket におけるポートの共有は、(nginxの場合)

location / {
    proxy_pass http://localhost:8000;
    proxy_http_version 1.1;
    proxy_set_header Upgrade $http_upgrade;
    proxy_set_header Connection "upgrade";
}

このような形での http アップグレートして proxy_pass で飛ばしてあげれば問題ないが、socket.io を利用しての接続の場合は、XHRやポーリングなどの他のプロトコルでの接続などもあるために、/socket.io/ という location が予約されているので、上記の location の部分を、

localtion/socket.io/ {

という形に変更してやることで、socket.io の、back proxy化が可能となる。

Edit
変数スコープについて

JavaScriptの変数スコープは、this問題と絡んでわかりづらいので、ここで簡単に説明する。また、一般的に説明されている、定義は関数範囲というのは、ここでは割愛する。

Edit
require で読み込んだファイル内でのスコープ

  1. requireで読み込んだファイル内で、直接 var test などと記述した変数は、外部から一切アクセスできない。ゆえに、ファイル内だけのグローバルのようにしか利用しない変数などの場合は、非常に有効に活用でき、それらの値も内部的には保持されているため、関数内だけで外部から一切アクセスさせたくない場合には便利。ただし同じファイルの場合は、値が共有されるため同じファイルを別々に読み込んでも、内部の値は共有される。
<読み込みファイル(MyFunc)内>
var test;                <- 外部から一切アクセス付加

var Myfunc(){
    test = "only file inside"        <- ファイル内だけのスコープ
    this.test = "instance value";    <- 下記で説明する new 後にアクセス可能
}
  1. ファイル内で this.test などと定義された変数は、外部でそのファイルを new で実体化させたときに、その実態オブジェクトの変数として管理されるので、下記のように作成された場合などは、下記のアクセスで操作できる。
var Myfunc = require("MyFunc");
var my = new Myfunc();

my.test = "new word";        <- このように、this.test が操作可能

Edit
Prototypeの扱い。

詳しくはhttp://qiita.com/takeharu/items/809114f943208aaf55b3ここ。

ただし、プロトタイプチェーンに関しては、http://d.hatena.ne.jp/cou929_la/20100929/1285770930ここを参照。

Edit
管理やライブラリやツール

Edit
npm(Node Package Manager)

node用のパッケージ管理ツール。yarnなどもあるが今の所、npmが主流。

Edit
プロジェクトフォルダを作ったら最初に行う初期化

# npm init -y

Edit
利用パッケージのインストール

# npm install <パッケージ名>

installコマンドでカレントフォルダの ./node_modules にインストールされる。(但し -g オプションの場合はグローバル環境に行われる)

オプション概要
-gグローバル環境に追記され、コマンドパスも通る。
--savepackage.json の dependencies に追記される。
--save-devpackage.json の devDependencies に追記される。
--save-optionalpackage.json の optionalDependencies に追記される。

package.json と package-lock.json の2つが作られる。基本は package.json に利用されるパッケージの環境情報に関する設定が記述されるが、package-lock.json にはパッケージ同士のバージョン依存が管理される。(package-lock.jsonは npm5 から採用された)

※下位モジュールでのpackage-lock.jsonは無視される。

Edit
環境の使い分け

package.json に明記される。dependencies(製品環境)には、製品用のパッケージ情報を記述し、devDependencies(開発環境) にはビルド時だけしか使われないようなツールやライブラリを記述されるのが一般的。また npm install オプションで、--production を指定すると、製品環境にのみインストールされる。

Edit
パッケージの再インストール

# npm install

パッケージ名を指定しないと、package.json に記述されているモジュールを自動的にすべて再インストールする。(Railsの bundle installと同じ)

Edit
パッケージ名について。

パッケージ名に、「 - . _ 」 が利用されても無視して結合した文字列でパッケージ名のユニーク性を検証するため、package-code と、package_code、package.code などは、すべて packagecode というパッケージの扱いとなる。

また @scope/mypackage というように、@の次にスコープを指定できる。たとえば自分の組織名であったり、またはパッケージの所属するグループであったり、パッケージのhグループ分けと考えてよいだろう。/ の次は今までと同じパッケージ名となる。

これらのパッケージ名のルールは、増え続けるパッケージや類似品に対する規約のようなものとなる。ちなみに、ソースから参照する場合は

require('@scope/mypackage')

というように指定可能となる。

Edit
npx

npmと一緒にインストールされる。npm install -g のように、-g オプションでインストールされたコマンドは、OSのパスに通されるが、環境汚染があるため -g を使わない場合に、個別プロジェクトにインストールされたコマンドが実行できる。

Edit
webpack 4(ビルドツール)

Edit
必要モジュール

webpack-cli は webpackコマンドを利用するために必須インストール。 webpack-dev-server は

インストール

# npm install webpack-cli webpack-dev-server

Edit
設定ファイル

webpackの設定ファイルは webpack.config.jsに記述される。最低限下記の設定は必要となる。(output で path を指定しないと、dist フォルダに出力される)

module.exports = {
    entry: "./src/main.js",
    output: {
        filename: "bundle.js"
    }
}

開発用(source.map有)と製品用(minify)を mode で分ける。(webpack4 からの機能)

module.exports = {
    mode: "development",      <- "production"だと製品版
    devtool: 'source-map'     <- sourcemapファイル(.map)を作成する。
}

webpackの設定ファイルを作成しなくとも

# npx webpack src/main.js dist/bundle.js

のように指定することは可能。

Edit
parcel(ビルドツール)

webpack よりも簡単にビルドできる。ビルド後自動的に web サーバーが起動する。

# parcel index.html

このようにTOP htmlを指定するだけで、ソースを解析して関係するファイルをまとめてくれるため、設定ファイルが必要無い。

オプション概要
-p開発用webサーバーのポート番号指定 (初期値は1234)
watchHMR(hot module replacement)のみ。開発用webサーバーは起動しない
build本番環境向けのバンドルのみ行う。HMR, 開発用webサーバーは起動しない

これも webpack のように、./dist フォルダにビルドファイルが保存される。

※厳密には parcel にも package.json に設定を記述して細かい設定ができるようである。

Edit
PythonによるWEBブラウザ起動

簡単なテストにすぐに使えるWEBサーバー

#  python -m SimpleHTTPServer 8888

起動したカレントフォルダ直下をルートディレクトリとして起動する。

Edit
libuvのコンパイル

# ./autogen.sh
# ./configure         <- arm用の場合 --host=arm
# make
# make install

Edit
gypのインストール

# git clone https://chromium.googlesource.com/external/gyp
# python setup.py install

Edit
nginxをロードバランサーにしている場合。

# vi nginx.conf
 upstream web_sockets {
     ip_hash;                                                    <- これが無いと接続時同じサーバーにならない。
     least_conn;
     server host01:9090 weight=5;
     server host02:9090;                               <- 複数記述でロードバランス
 }
 server {
      location /ws {
           proxy_http_version 1.1;
           proxy_set_header Upgrade $http_upgrade;
           proxy_set_header Connection "upgrade";
           proxy_pass http://web_sockets;
           proxy_cache_bypass $http_upgrade;    <- これで http を、websocket対応に。
           proxy_read_timeout 24h;                    <- これを付けないと 60秒で勝手に切断される。
       }
 }

Edit
proxy を利用している場合、websocketでのremoteAddressがproxyサーバーのアドレスになってしまう。

<nginx.confのproxyに以下を追加>

proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;

<node.js側で>

var ip = req.headers["x-forwarded-for"];