node.js 筆記
如果單純按 http://nodejs.org/api/addons.html 的方法去寫具回傳函數的 addon,addon 仍是同步及 blocking 的。
C/C++:
#define BUILDING_NODE_EXTENSION #include <node.h> using namespace v8; Handle<Value> RunCallback(const Arguments& args) { HandleScope scope; Local<Function> cb = Local<Function>::Cast(args[0]); const unsigned argc = 1; Local<Value> argv[argc] = { Local<Value>::New(String::New("hello world")) }; cb->Call(Context::GetCurrent()->Global(), argc, argv); return scope.Close(Undefined()); } void Init(Handle<Object> target) { target->Set(String::NewSymbol("runCallback"), FunctionTemplate::New(RunCallback)->GetFunction()); } NODE_MODULE(addon, Init)
JavaScript:
var addon = require('./build/Release/addon'); addon.runCallback(function(msg){ console.log(msg); // 'hello world' });
如要寫成非同步,需利用 libuv 提供的 uv_queue_work 去處理,將程序拆成進入點,非同步部分,及完成後的處理部分。
C/C++:
#include <node.h> #include <node_buffer.h> struct Baton { uv_work_t request; Persistent<Function> callback; bool error; int value; // 可另加自定義變量 }; void AsyncWork(uv_work_t* req) { Baton* baton = static_cast<Baton*>(req->data); // ... 需時處理的非同步部分 } void AsyncAfter(uv_work_t* req) { Baton* baton = static_cast<Baton*>(req->data); // ... 完成後處理及傳回 node.js,argc 及 argv 為傳入 callback 的資料 baton->callback->Call(Context::GetCurrent()->Global(), argc, argv); } Handle<Value> RunCallback(const Arguments& args) { // ... node.js 的進入點,args 為傳入資料,應放到 Baton 內 Baton* baton = new Baton(); baton->request.data = baton; baton->callback = Persistent<Function>::New(Local<Function>::Cast(args[1])); baton->error = false; // ... 準備工作 int status = uv_queue_work(uv_default_loop(), &baton->request, AsyncWork, AsyncAfter); return Undefined(); } static void Init(Handle<Object> target) { target->Set(String::NewSymbol("callback"), FunctionTemplate::New(RunCallback)->GetFunction()); } NODE_MODULE(addon, Init)
JavaScript:
var addon = require('./build/Release/addon'); addon.runCallback(function(msg){ console.log(msg); });
而三個組件之間的溝通,則利用結構體去傳遞(如上文的 baton),次序為 RunCallback -> AsyncWork -> AsyncAfter,於 AsyncWork 內不應使用 v8 的函數。
但要注意,由於 node.js 是單線程,就算非同步,但執行時仍不可跟其他程式碼同時執行,解決方法為 Threading 或使用 Cluster 等方式。
參考資料:
https://github.com/kkaefer/node-cpp-modules
http://kkaefer.github.com/node-cpp-modules
http://www.slideshare.net/nsm.nikhil/writing-native-bindings-to-nodejs-in-c
二、監察 node.js 的垃圾回收
node.js 的 v8 JavaScript 引擎有自動垃圾回收機制,它在背後執行,若要監察它的運作,可安裝 memwatch 這個 addon。
var memwatch = require('memwatch'); memwatch.on('stats', function(stats) { console.log(stats); });
聆聽 stats 這個事件,便可得知 node 何時進行垃圾回收。而我發現 node.js 在持續高負荷之下,便不會進行垃圾回收,這樣,用完的資料便不能清除,而記憶體使用量便會一直上升。memwatch 這個 addon 提供了 gc 的方法,在這情況下手動使 node.js 進行垃圾回收,或許是一種解決方法。
memwatch.gc();
另可參考:
node-memwatch-demo
此外,我亦找到一個 node.js 的 addon,叫做 node-weak,可令程式內的變量製造 Weak Reference,縱然變量非 null,node.js 亦可隨時將參照的資料清除。而不像一般變量般,只要參照著一些資料,資料便不會被回收。給我最大的用處是監察垃圾回收的情況。
例子:
var weak = require('weak'); function test(){ var a = {"i": 0}; var b = weak(a, function(){ console.log("data will be garbage collected"); } }
b 便是 a 指著的資料的 Weak Reference,執行 test() 後,變量 a 會消失,當 node.js 進行垃圾回收,由於原本 a 指著的資料再沒有 Strong Reference ,所以可以被清除,到時候 "data will be garbage collected" 便會出現,說明那些資料會在即將發生的垃圾回收中清除。
而我發現以下寫法,由於 node.js 不會知道 testObj 內的資料會否再被引用,所以 "testObj will be garbage collected" 和 "testObj.a will be garbage collected" 並沒有出現,即是它們沒有被清除。而類似的寫法在 JavaScript 或 node.js 很普遍,所以要留意。
var testObj = {a: {b: "a"}}; weak(testObj, function(){console.log("testObj will be garbage collected");}); weak(testObj.a, function(){console.log("testObj.a will be garbage collected");}); function doSth(testObj){ var intervalFunction = function(){ var b = testObj; b = null; } var interval = setInterval(intervalFunction, 2000); setInterval(function(){ clearInterval(interval); },5000); } doSth(testObj);
但改成這樣子卻可以,也就是在 clearInterval 時將 testObj 指向 null,原本的資料再沒有其他變量引用,故此能被回收,有點奇怪吧:
var testObj = {a: {b: "a"}};
weak(testObj, function(){console.log("testObj will be garbage collected");});
weak(testObj.a, function(){console.log("testObj.a will be garbage collected");});
function doSth(testObj){
var intervalFunction = function(){
var b = testObj;
b = null;
}
var interval = setInterval(intervalFunction, 2000);
setInterval(function(){
clearInterval(interval);
testObj = null;
},5000);
}
doSth(testObj);
還有個有用的是 node-webkit-agent,可以幫忙做 profiling。
三、JavaScript 中的 this
參考資料:
http://dreamerslab.com/blog/tw/javascript-this/
http://dreamerslab.com/blog/tw/javascript-function-scopes-and-closures/
http://blog.darkthread.net/post-2009-03-11-js-this-and-closure.aspx
http://blog.ericsk.org/archives/1360
http://www.quirksmode.org/js/this.html
http://javascriptweblog.wordpress.com/2010/08/30/understanding-javascripts-this/
http://joshuakehn.com/2011/10/20/Understanding-JavaScript-Context.html
http://www.bennadel.com/blog/2265-Changing-The-Execution-Context-Of-JavaScript-Functions-Using-Call-And-Apply-.htm
四、其他連結
http://nodejs.org/
http://asyncionews.com/
http://toolbox.no.de/
http://docs.nodejitsu.com/
http://howtonode.org/
http://fred-zone.blogspot.hk/
將 file 或 socket handle 傳給另一 node.js process 的 addon
本文連結