Noder eyes for frontend guysby Fillano
先說聲抱歉 Orz
關於題目
● 源自 "Queer eyes for straight guys"● 澄清一下
○ 寫node.js不表示比較厲害,只是希望能讓熟悉瀏覽器的Javascript coder可以知道進入node.js需要的知識跟常見的難點
○ I'm not queer, but I do have some friends.● 另外...
這個投影片不會講到
● node.js的教學● 各個module怎麼用的詳細說明● 怎麼做出XXX(忍不住還是會...)
這個投影片會專注在
● 寫node.js程式需要的關鍵知識● 如何開始寫node.js程式的一些提示
開始吧...
window物件在哪?
● 可能情境:裝完node.js,進入shell,不知道有什麼可以使用
● 在瀏覽器中,一切都可以從window物件開始,那在node.js中,有什麼相同的東西?
● 這要從Javascript的基本特性談起...
Javascrip基本:依賴於host環境
● 核心物件之外,所有能用的功能,都放在Global中
● 在browser中,Global就是window物件● 在node.js中,Global就是global物件● 檢查一下:test001.js● 有三個同名的物件,實際上都是global物件● 檢查一下:test001a.js● 但是呢...
node.js對於Global做了一點調整
● 操作global物件,可以反應在global scope● 但是直接操作global scope,並不會影響到
global物件● 這跟在瀏覽器環境中的行為不太一樣● 檢查一下:test001b.js● 在瀏覽器中:test794.html● 那麼...
global物件到底能做什麼
● 只有:○ 操作TypedArray需要的相關物件
○ process物件
○ Buffer constructor○ setTimeout/setInterval相關函數
○ console物件,用來作stdio
● 檢查一下:test002.js● 接下來咧?好像沒啥東西...
那其他功能怎麼辦?
● 其實還有require這個核心函數,可以用它來載入模組
● 檢查一下:test003.js● 後面還會詳細解釋一下模組的運作● 了解了global物件,再看一個偶爾會有人問的
問題...
我可以使用jQuery嗎?
● 通常我會想:別傻了,你在寫伺服器程式● 不是沒有解(JSDom...)● 但除了做蜘蛛,通常不會用到● 下一個問題也有不少人問...就是...
什麼?單一執行緒?
● 用關鍵字應該可以找到不少這方面的討論● 不過這個問題說來話長...下一頁繼續
其實,single thread是寫Javascript的必備
知識
● 參考○ Resig: How JavaScript Timers Work○ Zakas: Professional Javascript for Web
Developers: p. 598 Advanced Timers○ Opera: Timing and Synchronization in
Javascript○ HTML5: HTML5 Spec - 6.1.4 Event loops○ YDN: Understanding event loops and
writing great code for Node.js
歸納起來,就是
● 不論是非同步的事件、timer、UI parsing等● browser所載入的每個網頁,你寫的Javascript
都是在一個thread裡面跑● 在node.js也是這樣
所以,Javascript到底怎麼跑?
● 可以把Javascript的執行過程想像成a. 執行top most的程式碼(主要是global scope,以及此時
執行的函數)
b. 進入event loop
c. 一有有事件程式放進event queue,event loop就會取出來執行
■ 由timer觸發的,不會在時間到之前執行,但是可能會更慢
■ 由event觸發的,就是儘快執行
● node.js的特異功能a. process.nextTick():會盡量排在event queue前面讓他
可以先執行,而不是從後面填入
這不是例外,但其實你有機會碰到別的
thread
● 在網頁端:○ frame/iframe○ web worker
● 在node.js○ sub process○ threads-a-gogo模組(這是真的thread,只能透過
message溝通,目前還無法做什麼事)
● 測試:test644.html(WebWorker的global跟網頁中完全不同)
在底層,還有更多東西必須使用thread
● 寫Javascript不會直接接觸...但是在機制上○ 瀏覽器:XMLHttpRequest底層
■ onreadystatuschange● 網路I/O在另外一個thread跑,根據處理的狀態
變化,才把叫onreadystatuschange事件處理函數塞進event queue
○ node.js:I/O底層■ fs.read■ fs.write
● 所以,single thread是對Javascript Coder必須的概念,但是引擎實作並不是這樣
single thread的方便性
● 同一時間不會執行兩段程式○ 沒有race
○ 不需要lock
○ 寫非同步比較簡單
● 但是我們寫的code可能會...
程式寫壞了,就全部塞住
● 這不論在瀏覽器或伺服器端都一樣,某個
function中的程式卡住(ex. 無窮迴圈),就全部卡住了
● 在node.js,沒有catch到的exception,會讓程式
exit,但是沒exception照樣可能卡著(ex. 無窮迴圈)
● 就算沒卡住...
程式寫不好,伺服器反應就全部變慢
● 沒那麼糟,但是不優● 但是有時...就是得做花時間的事情● 有什麼辦法?
所以有些事可以慢點做
● 程式打死結(ex. 無窮迴圈),是一定沒救● 但是某個事件處理程式跑太久,還是會阻礙別
人● 在Browser跟node.js解法都一樣,make it async
by do something later● 使用setTimeout()/setInterval()● 使用process.nextTick()● 在single thread中跑的程式,就像DOS時代的合
作式多工...
不過async會讓邏輯更複雜XD
● 這在node.js常碰到,建議要習慣用async的邏輯來思考事情
● 有一些解決方法,容後再述● 另外的問題是,程式都在同一個thread跑喔,
可是...
node.js在伺服器端執行耶!?
● 這不是瀏覽器...● 如果只有單核心,再多thread也只會用到一個
cpu核心的資源,就沒差● 可是,我的伺服器有四路八核心cpu,總共有32
核心,但是node只會用到一個...● 解法在v0.6.x之後出現...容後再敘● 有了一些基本認識,可以開始coding了...不過一
頭鑽進去會發現...
hello world...然後我要做什麼?
● hello world總是很美好XD● 但是很遺憾...http模組幫你做的事情不多● 因為他沒做,所以你需要基本的http知識
○ http request○ http headers○ session相關
■ cookie header■ setcookie header■ 怎麼做出伺服器端session
○ MIME○ ...
● 另外,沒人幫你map靜態檔案● ...
對於新手來說,單靠node.js核心模組是
不可能寫出功能完整的伺服器Orz
● 想像一下,你要用幾個基本的物件做出apache httpd伺服器,工程很大的...
● 我自己為例,之前參加ITHelp鐵人賽,花了一個月還只有架構
● 所以要快的話,還是得先靠第三方模組○ Connect middleware可以做基礎○ 一些Web Framework
■ Express■ others
● Meteor● Mojito● ...
● 那要怎樣找模組?...
模組管理工具:NPM
● NPM(Node Package Manager)● 原本是第三方的解決方案● 目前已納入node.js的binary distribution中● 怎麼找模組?
○ `npm search KEYWORD` (常會用完記憶體)
○ http://search.npmjs.org/ (官網,但是很慢)
○ https://github.com/joyent/node/wiki/modules (這是分門別類的目
錄,但是更新可能比較慢)
○ nipster (很快,而且用評分排序)
● 之前測試過...test003.js可以看到一些線索● 模組預設搜尋路徑
○ 目前工作目錄下的node_modules目錄
○ require.main.paths陣列中的目錄
● npm list -g顯示的路徑加上node_modules○ 預設
■ UNIX: /usr/lib or /usr/local/lib■ Windows: %APPDATA\npm
● 用npm安裝/管理模組會使用的路徑○ 沒有加-g:目前路徑
○ 有加-g:npm list -g顯示的路徑
node怎麼找到模組
想使用global module
● 如果(不建議)希望可以直接使用● 需要設定NODE_PATH環境變數,指向npm list
-g使用的模組目錄 + node_modules
使用了模組...怎麼維護相依性?
● 我們使用npm,npm使用package.json檔● package.json(模組根目錄中)
○ 不管寫不寫模組,最好都知道裡面定義的各個欄位○ 跟執行相關
■ 定義模組的entry檔案■ 定義模組的相依性
● 許多node.js hosting service,都支援package.json,在deploy的過程中,會幫你把相依的模組裝好
● 參考:○ http://package.json.nodejitsu.com/○ npm help json
node.js的模組載入機制與注意事項
● require是同步執行的○ 呼叫require時,要等到模組載入完畢,才會執行下一行
程式
● module是在沙箱中跑的○ 所以,模組裡面沒有辦法動到主程式○ 主程式要用到的東西,全部都要export(module.exports
或exports)
● 執行過一次require後,模組就會被cache起來○ 所以,修改過的module,要重新啟動程式才會有作用
但是我希望程式可以在修改後不停機自
動apply耶...
● 最好讓host服務替你做,省功夫● 手動做的話,可以考慮使用cluster● 驗證一下:test005.js
○ 由於每個worker都是獨立的process,kill掉再重新執行,就會使用新的程式了
○ 缺點是:master無法更新...○ 仔細想一想:如果把worker內的程式寫成模組來載入,
然後master用fs.watch模組的目錄,然後每次deploy都更新這個模組...其實就類似hosting的做法了
但是,我需要AMD的話怎麼辦?
● 參考:require.js● 其實,你可以自己做● 例如:node-amd-loader● 重點:
○ node只提供了核心模組○ 沒人告訴你其他的不能自己做○ 忘記那些爭論吧(阿?有爭論?)
我需要物件導向嗎?
● node.js常用的作法:○ 使用util.inherits(),這是給constructor來用的喔○ 最常使用的地方:需要使用到事件機制,就要繼承
EventEmitter
● 其他...就任意...Javascript有多種作法● 參考一下良葛格的意見
○ 程式語言的特性本質(三)-從消弭重複性看封裝、繼承、多型
● 再來是另一個常被問到的問題...
有辦法避開callback地獄嗎?
● 這是無法避免的,請練習async的邏輯思考● 可以透過適當的調整來改善
○ 取代匿名函數callback(這是一個過程,這樣還可以方便單元測試)
■ 改用非匿名的函數
■ 進一步利用模組帶入這個函數
○ 利用flow control來管理函數,讓你可以控制數個函數
執行過程的關係,例如某幾個函數要順序執行、某幾個函數要平行執行等
有辦法避開非同步地獄嗎?
● 同樣難以避免● 而且,在執行上有時會碰到邏輯上的困難點● 解決方式如前述● 那麼...
什麼是flow control?
● 函數在Javascript是一等公民,所以可以用函數來處理函數
● 測試一下:test006.js○ 執行三個函數○ 全部執行完畢,才執行某個函數來處理結果
● 除了使用flow control,要能使用flow control的函數,通常要改寫成cps的形式
● cps (continuation passing style)○ 簡單地說就是傳入一個callback○ 在函數結束時(可能不只一處),呼叫這個callback,有
返回值時,需要把返回值傳給它
關於flow control的好文章
● Tim Caswell○ Control Flow in Node○ Control Flow in Node Part II○ Control Flow in Node Part III
● Mixu's tech blog○ Essential Node.js patterns and snippets○ 我的例子用這個改的,因為邏輯最簡單
● 一些模組○ https://github.com/creationix/step○ https://github.com/caolan/async○ https://github.com/laverdet/node-fibers
另外一個讓程式碼更有結構又乾淨的方
式
● CoffeeScript● ...我沒在用,我已經太習慣Javascript了XD● 早上有人講這個喔(高見龍 / 龍哥)● 另外一個寫伺服器會發現的,是關於...
gloal variables
● 以http server為例,每個request是一個事件● 要維護cross request的狀態(ex. session),用
global variable就可以做到(其實不一定,要看事件處理函數定義的位置)
● 不過當需要scale的時候,這個作法就會出問題(ex. cluster)
● 所以,共享的狀態,儘量用外部實現比較好(memcache, mongodb etc.)
● 好,終於要來看怎麼解決無法利用多核心cpu的...
cluster模組
● since v0.6.x● 解決single thread無法充分利用多核心的系統
優勢的問題● 使用多個sub process,共享同一個port● master/worker之間,透過message來溝通● 測試一下?(之前測過了XD)
cluster不是真的scaling
● 不要被cluster這個名字拐了● 真正要scaling,請使用reverse proxy,例如:
○ nginx○ node-proxy模組 (效能不如nginx)
● 基本上,不論使用怎樣的程式語言,大多數伺服器都可以這樣scale
● 另外,許多PAAS(node.js hosting)會提供方案及工具來做
我要怎樣debug?
● 建議把邏輯集中到function,放進module,做單元測試(這是基本的refactoring)
● 簡單的邏輯bug,用console.log、console.dir就可以解決
● 如果需要做profiling、找出memory leak,再來使用debug工具,例如○ Using Eclipse as Node Applications Debugger○ other solutions: node-inpector, ndb etc.
有什麼hosting服務嗎?
● 有許多廠商提供這方面的服務○ Joyent (MiCloud)○ Heroku○ Nodejitsu○ Nodester○ Azure○ more...
● 選擇重點○ 基本管理工具
○ deploy方式○ 擴充方案
兩三事代替總結
● 這些主題,有很多是在nodejs group上常見的● 而且,裡面有些東西是火藥庫● node.js開發team的目標,是一個精簡而可靠
的核心,所以會放棄很多用第三方模組就可以做到的功能
● 有需求的地方,就是可以自己插手的地方,畢竟node.js還很年輕
● 後面某一場,Ben會分享他為什麼要開發自己的framework,大家可以聽一聽他的心得
Q&A
我最近的出沒地點
● 我的Facebook● 我的噗浪● facebook Javascript.tw group● facebook nodejs.tw group● facebook PHP 台灣 group● Zen of Friends讀書會以及Web Dev Party● 我的部落格 (自從用噗浪以後就少出沒了)
● 歡迎來交朋友...
The End