用C++实现nodejs Add-on

766 阅读3分钟

准备

nodejs和v8之间的关系

nodejs官网:

Node.js® is a JavaScript runtime built on Chrome's V8 JavaScript engine.

Node.js是一个事件驱动I/O服务端JavaScript环境,基于Google的V8引擎,V8引擎执行Javascript的速度非常快,性能非常好。

NodeJs本身核心部分就是用C++编写的,在C++中嵌入V8引擎,这样业务逻辑可以用简单的、效率很高的js去编写。 扩展nodejs本质上就是扩展v8 engine,最终让nodejs可以使用这些扩展的部分。

NodeJS可以看做是一个大的中间件,NodeJs内部也提供了很多中间件。

有了这个强大的内核,再加上渲染引擎WebKit,js能做的事情越来越多

VS Code 也是基于 Electron (原来叫 Atom Shell) 进行开发的。Electron 基于 Node.js(作为后端运行时)和 Chromium(作为前端渲染)。 www.electronjs.org/docs

开发准备

  • 入门v8引擎c++开发示例 github.com/v8/v8/tree/…
  • 建议使用linux环境,自带gcc
  • 安装 node-gyp,和GYP(Generate Your Project)一样node依赖其他语言编写的应用时需要以平台环境现场编译。

Add-on实现

distance.cc

#include <cstdlib>
#include <cmath>
#include <nan.h>
#include <v8.h>

using Nan::AsyncQueueWorker;
using Nan::AsyncWorker;
using Nan::Callback;
using Nan::HandleScope;
using Nan::New;
using Nan::Null;
using Nan::To;
using std::pow;
using std::sqrt;
using v8::Function;
using v8::Local;
using v8::Number;
using v8::Object;
using v8::String;
using v8::Value;

struct Point {
  double x;
  double y;
};

double CalculateDistance(Point* pointA, Point* pointB) {
  return sqrt(pow(pointA->x - pointB->x, 2) + pow(pointA->y - pointB->y, 2));
}

class DistanceWorker : public AsyncWorker {
 private:
  double distance;
  Point* pointA;
  Point* pointB;

 public:
  DistanceWorker(Callback* callback, Point* pointA, Point* pointB) :
    AsyncWorker(callback), pointA(pointA), pointB(pointB) {}

  ~DistanceWorker() {
    delete pointA;
    delete pointB;
  }

  void Execute () {
    distance = CalculateDistance(pointA, pointB);
  }

  void HandleOKCallback () {
    HandleScope scope;

    Local<Value> argv[] = {
      Null(),
      New<Number>(distance)
    };

    callback->Call(2, argv);
  }
};

NAN_METHOD(CalculateSync) {
  Local<Object> js_pointA = To<Object>(info[0]).ToLocalChecked();
  Local<Object> js_pointB = To<Object>(info[1]).ToLocalChecked();

  Point* pointA = new Point();
  pointA->x = To<double>(js_pointA->Get(New<String>("x").ToLocalChecked())).FromJust();
  pointA->y = To<double>(js_pointA->Get(New<String>("y").ToLocalChecked())).FromJust();

  Point* pointB = new Point();
  pointB->x = To<double>(js_pointB->Get(New<String>("x").ToLocalChecked())).FromJust();
  pointB->y = To<double>(js_pointB->Get(New<String>("y").ToLocalChecked())).FromJust();

  info.GetReturnValue().Set(CalculateDistance(pointA, pointB));
}

NAN_METHOD(CalculateAsync) {
  Local<Object> js_pointA = To<Object>(info[0]).ToLocalChecked();
  Local<Object> js_pointB = To<Object>(info[1]).ToLocalChecked();
  Callback* callback = new Callback(info[2].As<Function>());

  Point* pointA = new Point();
  pointA->x = To<double>(js_pointA->Get(New<String>("x").ToLocalChecked())).FromJust();
  pointA->y = To<double>(js_pointA->Get(New<String>("y").ToLocalChecked())).FromJust();

  Point* pointB = new Point();
  pointB->x = To<double>(js_pointB->Get(New<String>("x").ToLocalChecked())).FromJust();
  pointB->y = To<double>(js_pointB->Get(New<String>("y").ToLocalChecked())).FromJust();

  AsyncQueueWorker(new DistanceWorker(callback, pointA, pointB));
}

NAN_MODULE_INIT(Init) {
  NAN_EXPORT(target, CalculateSync);
  NAN_EXPORT(target, CalculateAsync);
}

NODE_MODULE(distance, Init)

binding.gyp

{
  "targets": [
    {
      "target_name": "distance",
      "sources": [
        "distance.cc"
      ],
      "include_dirs": ["<!(node -e \"require('nan')\")"]
    }
  ]
}

test.js

const Distance = require('./build/Release/distance');
let result;
let pointA = { x: 0, y: 0 };
let pointB = { x: 3, y: 4 };

result = Distance.CalculateSync(pointA, pointB);

if (result !== 5) throw Error(
  '#Sync: Result expected to equal 5 but instead got ' + result
);

console.log('sync calculation passed');

result = Distance.CalculateAsync(pointA, pointB, (err, result) => {
  if (err) throw err;

  if (result !== 5) throw Error(
    '#Async: Result expected to equal 5 but instead got ' + result
  );
  console.log('async calculation passed');
});
node-gyp rebuild # 编译C++
node test #运行效果

本文搬运:github.com/the-guild-o…

用java实现node扩展

Node.js想要和Java连接,需要一个 node-java 模块

www.cnblogs.com/zhuanzhuanf…

放弃用Go写node扩展

起初是有这个想法...

go不能为node直接写扩展,需要c++作为中间语言通知v8调用一下go生成的扩展,这个过程就涉及到一个问题,V8类型怎样转成go类型,试了多次 gofloat64这个类型v8是没有直接对应的类型的。所以需要特殊的方法进行类型转换。不适合入门实践。

args[0]->ToNumber(isolate);

blog.csdn.net/pipisorry/a…

重拾用Go写node扩展

require('node-go-require'); www.npmjs.com/package/nod…

see also

Nodejs cluster模块深入探究 segmentfault.com/a/119000001…

npx: node新增工具,可以临时执行命令,减少config.js的配置

juejin.cn/post/684490…