node-threadobject
v1.0.2
Published
let node support multi threads in js codes
Downloads
8
Maintainers
Readme
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.
支持的操作系统(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