node-threadobject

1.0.2 • Public • Published

Node-threadobject

deprecate Use muti-thread instead. 使用 C++ 技术写扩展,写起来比较难。muti-thread 模块是一个更好的解决方案,只需要用 Js 实现函数即可享受多线程的编程模型.

Node 在富计算场景下可能会遇到瓶颈(好比用一条腿走路)。 node-threadobject 是一个 C++ 模块,用来实现在 Js 代码中创建线程,并将复杂的计算任务委托给新线程执行。

在 Js 代码中创建线程对象,执行 CPU 密集型函数,例如计算大文件 HASH,加密解密等任务。可扩展 C++ 模块的处理函数,处理不同的复杂计算任务。在多核环境下,线程对象有助于更好的分配 node 集群中各个线程的 CPU 占用,以可控的方式减少线程等待和阻塞主线程。

例如,在双核环境下,一个线程对象使得其与主线程各运行于一个 CPU。在这种情况下,如果有两个线程对象处理计算任务,则主线程有 66% 的机会争取到运行。与使用线程池相比,能够精确预期。

在具体的场景中,例如使用 node 构建区块链(Block chain)的 P2P 网络,协议使得主线程面临着 CPU 计算的压力,比方说计算大块数据的 HASH。未经扩展的 node 在此类情景下可能遇到瓶颈。node-threadobject 是解决此类问题的通用方法。

此外,对于数据密集型业务,有可能引起频繁 GC,无法有效利用内存,例如 Buffer 与 String 的互转。如果考虑使用原生模块的技术处理,可有效减轻引擎负担。

同时,支持在新开的 V8 虚拟机里安全的运行一段 Js 代码。

node-threadobject is a package for providing ability to create new threads in js. It helps you consciously assign cpu-bound tasks to a limited number of CPUs.

Follow me on github!

支持的操作系统(Platform Support)

Windows, Linux and OS X

需要的 Node 版本 (Node engine)

>=4.0.0

编译链接(Compile & Link)

先全局安装 node-gyp
在工程根目录下运行 node-gyp configure
在工程根目录下运行 node-gyp rebuild
(编译过程大概需要 10s 左右,如果出现提示无权访问目录,请尝试 sudo node-gyp rebuild)

npm install -g node-gyp
node-gyp configure
node-gyp build (or  **sudo node-gyp rebuild** )

How it works

参考了 chrome 浏览器的线程模型(chrome thread model),每个线程内部包含了一个 C++ 闭包队列(C++ closure queue),按序处理任务。

增加更多的计算型函数 (Add more computational types of functions)

只需要增加新的文件,然后将头文件增加到rcib.h中。hash 是一个例子,它是一个无状态型的计算任务,file 是另外一个例子。增加新的计算型函数不需要修改 rcib(run codes in background) 目录里面的代码。

src/rcib 这个目录下的代码经过精心构建,通常扩展本模块不需要动里面的东西。

src/file 这是一个有状态计算型任务的范例。

src/hash 这是一个无状态计算任务的范例。

安装使用(Usage)

npm install node-threadobject (or  **sudo npm install node-threadobject** )

例子(Examples)

将一个定时器抛给线程对象,等待2秒钟后,回到主线程执行回调函数。内部使用64位表示时间,支持以小时为单位的大定时器

var Thread = require('node-threadobject');
var thread = new Thread();
 
thread.delayBySec(2, function(err){
  if(err)
    console.error(err);
 
  console.log('after two secs');
  thread.close();
  console.log('thread running state: ' + thread.isRunning());
  thread = null;
  });
 
console.log('thread running state: ' + thread.isRunning());
 
/*
result:
    thread running state: true
    after two secs
    thread running state: false
*/

在另一个线程中计算一个文件的 HASH 值

/*
 see test/example/sha2.js
*/
'use strict';
 
var path = require('path');
var fs = require('fs');
var assert = require('assert');
var Thread = require('node-threadobject');
var thread = new Thread();
thread.set_encode('base64');
 
console.log('HASH 计算之前');
fs.readFile('./test/thread.js', function(err, data) {
  thread.sha2({data, type: 256}, function(err, data){
    if(err) return console.error(err);
    console.log('HASH 计算结果');
    console.log(data);
    console.log('HASH 计算之后');
    console.log('正在排队处理的任务数:' + thread.numOfTasks());
  });
  console.log('正在排队处理的任务数:' + thread.numOfTasks());
});
 
/*
result:
    HASH 计算之前
    正在排队处理的任务数:1
    HASH 计算结果
    drK1C69gYX9I8qYOwWFPQLIo6FU/F++N/B9Rs5JsnYQ=
    HASH 计算之后
    正在排队处理的任务数:0
*/

上个例子的 Promise 版本

'use strict';
 
var path = require('path');
var fs = require('fs');
var assert = require('assert');
var Thread = require('node-threadobject');
const Promise = require('bluebird');
const co = Promise.coroutine;
 
var thread = new Thread();
thread.set_encode('base64');
 
fs.readFile('./test/thread.js', function(err, data) {
  co(function*(){
    var r = yield thread.sha2({data, type: 256});
    console.log(r);
  })();
});
 
/*
  result:
    drK1C69gYX9I8qYOwWFPQLIo6FU/F++N/B9Rs5JsnYQ=
*/

消息认证码(HMAC)

/*
 see test/example/hmac.js
*/
'use strict';
var path = require('path');
var fs = require('fs');
var assert = require('assert');
var Thread = require('node-threadobject');
var crypto = require('crypto');
var thread = new Thread();
var key = '_random_key_';
 
fs.readFile('test/thread.js', function(err, data) {
  thread.hmac({data, type: 512, key}, function(err, data){
    if(err) return console.error(err);
    console.log(data);
  });
  var hmac = crypto.createHmac('sha512', key);
  hmac.update(data);
  console.log(hmac.digest('hex'));
});
 
/*
result:
    9c2e2ddd685c05ddfdcc9f92194cb1308b17260ad09b12e259b1d8c4c3b61881b9faa10891f28f718a502347815d795793318c094edb504c5ac19ca0f5521895
    9c2e2ddd685c05ddfdcc9f92194cb1308b17260ad09b12e259b1d8c4c3b61881b9faa10891f28f718a502347815d795793318c094edb504c5ac19ca0f5521895
*/

Ed25519签名(EdDSA)

/*
see test/ed25519.js
*/
 
'use strict';
const Thread = require('node-threadobject');
var thread = new Thread();
 
thread.sign(new Buffer('a message'),'af9881fe34edfd3463cf3e14e22ad95a0608967e084d3ca1fc57be023040de59', function(err, data){
  console.log(data.toString('hex'));
});
 
/*
result:
    1b7b8d7141a2fd9e1fc99175eaa8dbcf82189d007a4210fc58f0cf24b5e0cc6d4f8b138352953a97d4237a9a75bfce97f5b12f7a8e56692ee7aafd61161f8204
*/

使用 V8 虚拟机运行一段 Js 代码

'use strict';
const Thread = require('node-threadobject');
var assert = require('assert');
 
var thread = new Thread();
 
var codeMain = `
function main(v){
    v = JSON.parse(v);
  return v.a + v.b;
}
`;
 
thread.runCode(codeMain, JSON.stringify({
        a: 5,
        b: 100
      }), function(err, res){
        assert.ifError(err);
        console.log(res);
});
 
/*
result:
 
105
*/
 

压力测试 (Pressure test report)

/*
 see pressure-test/mem-pressure-test.js
*/
'use strict';
 
var fs = require('fs');
var Thread = require('node-threadobject');
var thread = new Thread();
thread.set_encode('base64');
 
var fData = null;
function callback(err, data) {
  if(err) return console.error(err);
  console.log(data);
  setImmediate(function(){
    thread.sha2({data: fData, type: 256}, callback);
  });
}
fs.readFile('./mem-pressure-test', function(err, data){
  fData = data
  thread.sha2({data: fData, type: 256}, callback);
});

On Win 7 x86-64bit & node-v8.1.0

After 30 mins of running, mem usage maintained at ~16M

Benchmarks

多核电脑上,计算一个大文件的消息认证码,总计算量一致时,使用 threadobject 创建两个线程可以使得时间节约 50%

// benchmarks/slow.js 只使用一个线程计算
const thread = new Thread();
var r = yield Promise.all([
        thread.hmac({data, type: 384, key}),
        thread.hmac({data, type: 512, key})
      ]);

// benchmarks/fast.js 委托给两个线程分别计算
const thread1 = new Thread();
const thread2 = new Thread();
var r = yield Promise.all([
        thread1.hmac({data, type: 384, key}),
        thread2.hmac({data, type: 512, key})
      ]);

已包含的方法 (APIs)

close  //同步的关闭线程
isRunning  //返回线程对象的线程是否运行(存在)
delayByMil  //以毫秒延迟
delayBySec  //
delayByMin  //
delayByHour  //
initPrint  //初始化一个打印任务
printLog  //顺序打印
closeLog  //关闭打印
sha2  //SHA {256, 384, 512}
hmac  // {256, 384, 512}
numOfTasks  //线程队列里CPU密集型任务个数
makeKeypair // 使用 Ed25519 生成密钥对
sign // 使用 Ed25519 签名 Ed25519-DSA
verify // Ed25519 验证
runCode //使用 V8 虚拟机运行 js

Other example

// see test/

More descriptions

https://github.com/dazoe/ed25519.git (Ed25519 implementation)

证书 (License)

BSD

Package Sidebar

Install

npm i node-threadobject

Weekly Downloads

0

Version

1.0.2

License

BSD

Last publish

Collaborators

  • youjia