Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2945577
  • 博文数量: 441
  • 博客积分: 12019
  • 博客等级: 上将
  • 技术积分: 6175
  • 用 户 组: 普通用户
  • 注册时间: 2005-08-01 16:46
  • 认证徽章:
文章分类

全部博文(441)

文章存档

2019年(1)

2018年(10)

2017年(5)

2016年(2)

2015年(4)

2014年(4)

2013年(16)

2012年(48)

2011年(65)

2010年(46)

2009年(34)

2008年(52)

2007年(52)

2006年(80)

2005年(22)

分类: JavaScript

2013-07-27 17:00:10

今分析node.js 异步插件时,发现一篇繁体字文章,特转载于此,以便大家学习交流。
原文地址是:http://ailin.phychembio.com/miscellany/803/


一、以 C/C++ 編寫 node.js 的非同步 addon

如果單純按 http://nodejs.org/api/addons.html 的方法去寫具回傳函數的 addon,addon 仍是同步及 blocking 的。

C/C++:
#define BUILDING_NODE_EXTENSION
#include 

using namespace v8;

Handle RunCallback(const Arguments& args) {
  HandleScope scope;

  Local cb = Local::Cast(args[0]);
  const unsigned argc = 1;
  Local argv[argc] = { Local::New(String::New("hello world")) };
  cb->Call(Context::GetCurrent()->Global(), argc, argv);

  return scope.Close(Undefined());
}

void Init(Handle 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 
#include 

struct Baton {
    uv_work_t request;
    Persistent callback;
    bool error;
    int value;
    // 可另加自定義變量
};

void AsyncWork(uv_work_t* req) {
    Baton* baton = static_cast(req->data);

    // ... 需時處理的非同步部分
}

void AsyncAfter(uv_work_t* req) {
    Baton* baton = static_cast(req->data);

    // ... 完成後處理及傳回 node.js,argc 及 argv 為傳入 callback 的資料

    baton->callback->Call(Context::GetCurrent()->Global(), argc, argv);
}

Handle RunCallback(const Arguments& args) {
    // ... node.js 的進入點,args 為傳入資料,應放到 Baton 內

    Baton* baton = new Baton();

    baton->request.data = baton;
    baton->callback = Persistent::New(Local::Cast(args[1]));
 
    baton->error = false;

   // ... 準備工作

    int status = uv_queue_work(uv_default_loop(), &baton->request, AsyncWork, AsyncAfter);
    return Undefined();

}

static void Init(Handle 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。
阅读(2239) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
评论热议
请登录后评论。

登录 注册