- 13.07.19. masuilab
- http://glide.so/geta6
- @geta6
- 趣味: エロゲ
- glide.soというサービスを作りました
- Gistからスライドを生成してくれます
- h1とh2で区切ってくれる
- 今のところそれだけ
- みなさんjavascriptはご存知ですよね
- そのjavascriptを使ってサーバで書ける言語
- ブラウザいらない
- ファイルとかにアクセスできる
- 最新版を入れます (0.10.16)
- 更新頻度が高いので追いかける
- 0.10.16が8/16にリリースされました^^
- 古いバージョンは今すぐ
rm -rf
- OSX
brew install nodejs
- Linux
./configure && make && make install
- Windows
node.exe
をダウンロードする
node app.js
- 0 . 10 . 16
- 10をメジャーバージョン
- 16をマイナーバージョンと呼びます
- 1.*は、技術的な問題とは切り離してリリースされるそうです
- 奇数メジャーバージョンはインストールしない(7,9,11..)
- Nightlyなので、深刻なバグに遭遇しない限り放置でよい
- バージョンアップに強いコードを書く
- DocsのAPI Stabilityを見るのです
- 不安定なAPIに依存した実装をしない
- 例 nodejs.org - fs.watch
- 例 nodejs.org - vm
- DocsのAPI Stabilityを見るのです
- マイナーバージョンはバグフィックス、気づいたら入れる
- メジャーバージョンはAPIが変更される事もあるので要確認
- リリースノートを読む、大した量ではない
- blog.nodejs.org - node-v0-10-0
- どこかにまとめ記事があがるはず
いりません
- rubyistはすぐにバージョンマネージャを入れたがる
- もしあなたがNodeのコミッターで無ければ本当にいらない
- 隣接メジャーバージョンの互換性が高い
- マイナーバージョンではAPIが変更されない
- モジュールが動かなくなったら作ればいいじゃん
- 君からはNodeの匂いがしない
- 向いている分野からNodeの特徴を紐解く
- Nodeはネットワークプログラミングが得意です
- 特に、プロトタイピングやリアルタイム性の高いアプリ
- 非同期なプログラミングにとても強い
- 軽い処理を大量に処理する性能に優れる
- CPU的な意味で、重い処理には向いてない
- jsは元来I/Oを想定していない言語(ファイルアクセスとか)
- イベントループが言語単位で実装されている
- EventMachineのように食い合わせを気にしなくていい
- nodeはシングルプロセスで動作する
- ノンブロッキングI/Oを備えている
- リソースを100%食べられる
- 処理落ちが発生するが、処理待ちが発生しにくい
- ???
- イベントループ?ノンブロッキングI/O?
- なんなん?
牛丼屋店員のワークフロー
- 客が来る
- 半券を確認する
- 牛丼を作成する
- 牛丼を出す
- 次の客の半券を確認する
- 以下、ループ
- 今までは店員数を増やして対応するのが通常手段だった
- 人が増えたらフロアも調理場も増床するよね
- 牛丼屋は払う給与や管理費が増えコストがかさむ
- 店員数を増やしすぎれば牛丼屋は撤退してしまう
- (ちなみにこれがC10K問題)
- 店員は牛丼を準備している間にも他の作業をしてるよね
- 他の客の半券を確認したり
- 食べ終わった食器を下げたり
- つまり一人の店員がフル稼働できる
- 新たに客が来たらそっちの対応をする
- 半券を確認したら調理場へ戻って作業を続ける
- 牛丼の調理が遅れても次の客対応に向かえる
- と聞くと大仰だけど、つまり一人が頑張れる仕組みということ
- 長い処理を噛ませてもブロッキングが発生しない
- ちゃんと裏であくせく働ける
- 重い処理を噛ませればまた別の話
- Node is 社畜言語
- 例えばhttpのレスポンスで2秒待つと何が起こるか
- そのサーバに並列性100で100回アクセスしてみると..?
phpっぽい擬似コードで表す
$http.createServer(function() {
header('Content-Type: text/plain');
echo 'hello\n'; // hello
sleep(2); // 2秒待つ
echo 'world\n'; // world
})->listen(3000);
$ curl -i http://localhost:3000
HTTP/1.1 200 OK
Date: Mon, 19 Aug 2013 14:09:15 GMT
Transfer-Encoding: chunked
hello
world
- 2秒待ってからヘッダとhelloとworldが表示される
- 100回アクセスしたら終了までに2*100秒かかる
var http = require('http');
var s = http.createServer(function(req, res) {
setTimeout(function(){ // 2秒待つ
res.end('world\n'); // world
}, 2000);
res.writeHead({'content-type': 'text/plain'});
res.write('hello\n'); // hello
}).listen(3000);
$ curl -i http://localhost:3000
HTTP/1.1 200 OK
Date: Mon, 19 Aug 2013 14:09:15 GMT
Connection: keep-alive
Transfer-Encoding: chunked
hello
world
- ヘッダとhelloが表示される
- 2秒待ってからworldが表示される
- 処理完了前にレスポンスを送信可能
$ ab -n 100 -c 100 http://localhost:3000/
..中略..
Time taken for tests: 2.030 seconds
..中略..
- 100回同時にアクセスしても約2秒で全ての処理が終わる
- この2秒はsetTimeoutで待った分
- setTimeoutしてる間のCPU使用率は0
- もし他の作業があればそちらの作業に戻る
- 今回の場合はヘッダの送出とhelloの送出
- 同時に他のコネクションがあっても同じように動作する
- もし他の作業があればそちらの作業に戻る
- もし
sleep
のある言語だと、sleep中は反応しない- マルチスレッドの場合はスレッドを増やして反応する
- nodeには排他ロックも実行停止もない、ただアイドルになる
- 最高の夏をNodeと過ごそう
- Nodeは軽い処理をいっぱいさせるのに向いてる
- 分割可能なデータを処理させるのに向いてる
- MySQLで排他制御しつつ〜みたいな書き方は向いてない
- 他の言語と組み合わせる使い方が多い気がします
- 特にORMが必要な場合
- ruby + nodeとか、php + nodeとか
- そろそろNodeインストールできたと思います
- nodeと一緒にインストールされるnpmについて説明します
- Node Package Manager
- Node付属、モジュール管理用のコマンド
- 依存関係の自動解決や起動管理ができる
- モジュールを落としてきたり
- 自分で作ったモジュールを公開したり
- テストコマンドオプションを登録したり
package.json
の記述内容を元に動作する
- プロジェクトの名前や依存モジュール等を記述するJSON
- npmは親向きに最近傍のpacakge.jsonを探します
- ディレクトリを掘っていても正しく動作します
npm init
で生成できる
- ヘルプ表示
npm -h
|npm -l
- package.jsonの対話生成
npm init
- 補完
npm completion >> ~/.zshrc
npm i
|npm install
|npm isntall
- package.jsonに書かれた依存モジュールをインストール
npm i hoge
hoge
をインストールpackage.json
と同じ場所のnode_modules
に設置されるnode_modules/.bin
に実行ファイルがリンクされる
npm i hoge --save
hoge
をインストールしてpackage.json
に追記
npm i hoge -g
hoge
をグローバルインストール- npmのパスと同じ場所に実行ファイルがリンクされる
package.json
に実行ファイルの記載が無い場合はリンク無し
npm update
- インストール済モジュールを最新版へアップデート
- package.jsonにバージョン指定がある場合を除く
npm (start|stop|test)
- 各スクリプトの実行
[-g]はグローバルインストールしてねの意
- express [-g]
- 定番のWAF
- coffee-script [-g]
- CoffeeScriptが書ける
- nodectl [-g]
- 拙作、起動マネージャ
- mkdirp
mkdir -p
してくれる
- mktemp
mktemp
へのAPI
- mime
- ファイル名からMIME、MIMEから拡張子へ変換
- async-cache
- 非同期型のキャッシュ
- request
http.get
を便利にしてくれる
- mongoose
- MongoDBのODM(Object Document Mapper)
- passport
- oAuth周りの便利ライブラリ
- SNSへの個別対応がすごく充実してる
- moment
- 時刻計算のライブラリ
a minute ago
とかの文字列も生成可能
- 基本的には手探り
- 更新日付が最近のものを探す
- 現在のメジャーバージョンリリース以前のものは回避
- nodejsmodules.orgを見る
- 単純な機能・シンプルな名前のものを探す
- 基本的には手探り
npm info
してgithubのリポジトリを見る- インストールしてみて
README
を読む
- 難しくない、審査もない
npm adduser
する- package.jsonを詳しく書く
npm publish
でリリースされる.gitignore
に記述したファイルは無視される.npmignore
に記述したファイルも無視される
require
,exports
,module
を使います- CommonJSというサーバサイドjsの仕様に準拠
- コードを外部化したりするのにも使います
- 外部化したコードとモジュールの扱いに差はない
require
を使う
some_module = require('some_module');
myscript = require('./myscript');
exports
を使う
-- geta6.js --
exports.name = 'geta6';
exports.hobby = 'hentai game';
exports.dohobby = function () {
console.log(this.hobby);
}
-- app.js --
console.log(require('./geta6'));
{
name: "geta6",
hobby: "hentai game"
dohobby: [Function]
}
module.exports
を使う
-- geta6.js --
module.exports = (function () {
function geta6(){}
geta6.prototype.name = 'geta6';
geta6.prototype.hobby = 'hentai game';
geta6.prototype.dohobby = function () {
console.log(this.hobby);
}
return geta6;
});
-- app.js --
Geta6 = require('./geta6');
geta6 = new Geta6;
console.log(geta6);
geta6.dohobby();
{}
"hentai game"
- 別に違わない
- けど望ましい使い方はある
module.exports
が本体- prototypeを
module
と捉える - require先では
module
のインスタンスになる - インスタンス化しなくても使えるモジュールに使う
- prototypeを
exports.key = value
、シンプル
- 特定のオブジェクト型に用いる
- 独自のクラスなどを定義した場合に用いる
module.exports = [Function]
- 実はモジュールごとのローカル変数
delete
されても影響ありません
- ここに載ってる
- グローバルオブジェクト
- jsの場合、ブラウザウィンドウ
- Nodeの場合、自身のプロセス
- globalがwindow相当の予約語
- jsの場合、ブラウザウィンドウ
- あとはAPI(ファイルシステムなど)とか
- DOMやAPIに依存しないライブラリを共有できる
- momentやunderscoreなど
- デフォルト値の流し込みが容易になる
process.nextTick
setImmediate
- 主に再帰処理を行う場合
- イベントループをブロックしない
- 重いjsを実行するとブラウザは固まる
- nodejsも重いjsを実行するとサーバが固まる
- 他の処理が割込できる余地を残して実行する
http.createServer (req, res) ->
list = ( (dir) ->
list = []
for f in fs.readdirSync dir
if (fs.statSync f = path.join dir, f).isFile()
list.push f
else
list = list.concat arguments.callee.call @, f
return list
)(path.resolve './', req.url)
res.end list.join '\n'
.listen 3000
http.createServer (req, res) ->
list = ( (dir) ->
list = []
for f in fs.readdirSync dir
if (fs.statSync f = path.join dir, f).isFile()
list.push f
else
callback = arguments.callee
setImmediate ->
list = list.concat callback.call @, f
return list
)(path.resolve './', req.url)
res.end list.join '\n'
.listen 3000
- 予約時に実行中のイベントループが終了した時点
- 処理を次のイベントループに予約する
- process.nextTickを使った再帰処理はダメ
- 現在は警告+1000回の回数制限
- 将来的に削除される予定
NODE_ENV=production node app.js
する- nodectl使う
- 簡単に説明する
- データを受信したらすぐ実行、狂気のStream
- とても早いし便利
- Streamの大元はただのインターフェース
- なので、色々なStreamがある
- ReadableStream
- 読み込みStream
- チャンクを読み出すごとにイベントが発生する
- WritableStream
- 書き込みStream
- data
- chunkが来た
- end
- おわた
- error
- あぼーんした
file = fs.readFileSync 'copyfrom'
fs.writeFileSync 'copyto', file
copyfromの内容は全ていったんバッファされる
read = fs.createReadStream 'copyfrom'
write = fs.createWriteStream 'copyto'
read.pipe write
- チャンク毎にバッファ、都度破棄して書き込める
- pipeの左にRS、右にWSを置く
http.get 'www.google.com', (res) ->
data = ''
res.on 'data', (chunk) -> data += chunk
res.on 'end', -> console.log data
このres
はReadableStreamオブジェクト
http.createServer (req, res) ->
fs.createReadStream('file').pipe res
このres
はWritableStreamオブジェクト
- Streamが読み書き不可になった時
- イベントを補足、pauseしてresumeしないといけない
- これを自動でやってくれるのがpipe
- Streamはインターフェースクラス
- そのままextendしても機能しない
- 自分で実装する必要がある
- なので色々なStreamがある
- そこら中に溢れている
- 詳しいことは長くなるので割愛
- 後日やるかもしれない
- 小さいデータをたくさん処理する事例見当たらない
- アニメウィキでもスクレイピングしようか