先自我介绍一下,小编浙江大学毕业,去过华为、字节跳动等大厂,目前阿里P7
深知大多数程序员,想要提升技能,往往是自己摸索成长,但自己不成体系的自学效果低效又漫长,而且极易碰到天花板技术停滞不前!
因此收集整理了一份《2024年最新大数据全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!
由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新
如果你需要这些资料,可以添加V获取:vip204888 (备注大数据)
正文
Google的V8引擎是用C++开发的,NodeJS本身大部分基础Addon也是C++开发的,因此C++和NodeJS的结合比想象中容易得很多:
node和V8都提供了完善的C++ API来管理JS原生的类型,对象和模块, 所有的JS对象,函数,都能在V8的API中找到相对应的类型,如果同时了解C++和JS的语法,可以极快上手。
无需定义专门的语言绑定层,例如Java的JNI。这是由于JS的所有函数在V8引擎中,实际上都是同样形式(同样的返回值和参数列表),因此无须JNI那种复杂的方法签名机制, 于是Node就帮我们包办了标准C的输出接口,开发者只要将精力更集中在实际的C++逻辑上就好了。
最重要的,Node改造了Google的GYP(Generate Your Project)作为NodeJS addon的夸平台编译工具node-gyp。GYP本身就是特点就是简单易用,而它基于JSON格式的配置文件,更是对Javascript开发者极度友好。
说了半天,可能有人要骂 No BB, show me the code,程序员不来点实际的怎么行。接下来,介绍一下NodeJS的C++需要准备哪些东西,如何使用node-gyp,以及,如何将一个C++的class做成一个JS的class,变得和js对象的实例那样变得有生命周期,自己的方法等。
通过Addon增强NodeJS
对计算密集任务可以用C++写NodeJS的Addon来提高性能的优势。然而究竟应该怎么做呢?先给出一个addon文件的例子:
-rwxrwxr-x 1 melode 88920 Jun 17 22:32 meshtool.node
可以看到NodeJS的addon就是一个后缀为node的文件,这货实际上应该就是一个动态链接库,通过JS代码
var tool = require('./build/Release/meshtool');
即可加载。然而怎么得到这个东东?待我细细道来。
环境的准备
1. node-gyp
这个是C++ addon的跨平台构建工具,有了它我们才能在各个平台编译出.node文件。可以通过一个简单的配置文件binding.gyp描述编译的内容,如源代码文件,依赖的头文件目录,静态库目录,编译器参数等。然后在主流平台上,它都可以将你的配置转化为一套构建脚本(Makefile或VS工程).
获取它很简单,可以通过npm安装:
npm install -g node-gyp
如果没有安装tnpm,用npm安装也可以,注意,在内网指定阿里内部的目录镜像可以爽到飞起:
npm install -g node-gyp --registry=http://registry.npm.aliyun.com
装完以后就可以使用 node-gyp这个命令了:)
第一次用以前,建议调用如下命令先:
node-gyp install --ensure --disturl=http://npm.taobao.org/dist
上面那个命令是安装node-gyp编译所需的node和v8等依赖库文件。若不运行也会在第一次编译时自动运行。但事先通过指定disturl安装的话,节约了等待时间。
你可以通过运行
node-gyp -v
来检查命令是否成功安装。
到此node-gyp已经安装完毕,可以用来编译项目的addon了。
首先得先准备好binding.gyp, 可以放在你项目的任何目录下,但要注意
- 要和你准备运行node-gyp命令的那个目录一致
- 配置中的涉及到的文件或目录的相对路径要从该目录开始。
我们可以将它放在项目根目录。
配置文件的内容是一个大JSON,可以非常简单:
{
"targets": [
{
"target\_name": "meshtool",
"sources": [ “./meshfilereader.cpp”,"./index.cpp"]
}]
}
以上配置指定了一个叫meshtool的编译目标, 该目标需要编译的cpp文件为 index.cpp和meshfilereader.cpp.
接着在项目放binding.gyp的目录运行 node-gyp configure, 该目录下会生成一个build的目录,内含构建所需的规则和makefile.
于是我们再运行node-gyp build, 在运行目录/build/Release下我们便可以找到所需的.node文件了。
2. nan (Native abstraction for NodeJS)
当你开始写Addon不久以后,你便会发现一个令人抓狂的现实:NodeJS 的 0.12.x,0.11.x和0.10.x因为使用的V8版本不同,存在严重的API不兼容的情况。不管你愿不愿意,你发现只能做到令其中一个版本通过编译。你开始苦恼C++不能像JS代码那样,不用改一行代码就同时满足所有NodeJS版本, 直到你发现了nan.
nan 是老外的一个项目(不是"Not A Number"),解决了不同NodeJS版本间API的不兼容问题,同样可以用npm安装:
npm install nan
//or
npm install -g nan
也可以直接配置到package.json中。
安装完后要在binding.gyp中配置好nan的头文件依赖,修改如下:
{
"targets": [
{
"target\_name": "meshtool",
"sources": [ “./meshfilereader.cpp”,"./index.cpp"],
"include\_dirs" : [
"<!(node -e \"require('nan')\")"
}]
}
之所以include_dirs要这么写是因为这样可以自适应nan模块安装在全局和安装在本地的情况。如果确定安装方式的话,也可以直接写路径。
至此万事俱备,只欠代码。
编写Addon的C++代码
写代码前的预备知识
写Addon之前,建议先要了解一下Google V8的API,至少要了解以下的一些概念:
JS 基本类型 对应的V8原生C++ 原生类型 :
Javascript V8
Number v8::Number, v8::Integer
String v8::String
Array v8::Array
Object v8::Object
Function v8::Function
JS基本类型和V8的原生类型之间实际是等价的,也就是C++层从JS层获取到的JS基本对象和返回JS层的结果,都是以v8的上述的原生类型形式。
JS句柄 v8::Handle 它相当于一个智能指针,所有上面的C++的原生类型都是由Handle来引用的,相当于JS中的那个var 变量,因此不管是从JS层获取到的原生类型对象还是在C++内部构造出的原生类型对象,都是以 v8::Handle 形式给出来的。
v8::Handle分为两种,v8::Local和v8::Persistant, 前者只在当前Scope中有效,后者是代表全局变量。
v8::Local 的Scope由 HandleScope管理,由离最近的HandleScope分配,并随HandleScope生命周期结束而结束。而v8::Persistant的生命周期由自己的New和Dispose方法管理。
生命周期结束的Handle,其指向的对象会随时被垃圾收集回收。
JS方法的C++表示
必须为为全局函数或静态方法,根据V8版本不同固定为如下的形式:
V8 3.11
v8::Handle<v8::Value> AnyMethodName(v8::Argument args)
V8 3.28
void AnyMethodName(v8::Argument args)
JS方法的传入参数 v8::Argument
不管什么样的JS函数,其C++方法的传入参数都是一个v8::Argument对象,这是因为v8::Arugment本身就是一个list, 内含可变数量的实际参数,如果想取第i个传入参数,只需要使用args[i] 即可。另外还可以通过args.This()获取this对象。
了解完概念,我们试着写一个输出一个方法的Addon
和写普通的JS模块一样,Addon的代码需要确定模块的输出,这里就是借助Nan写输出一个叫parseMesh的JS方法的Addon:
NAN\_METHOD(ParseMesh)
{
NanScope();
if(args.Length() < 2 || !args[0]->IsString() || !args[1]->IsFunction())
{
return NanThrowError("Bad Arguments");
}
Handle<String> filename = args[0].As<String>();
Handle<Function> callback = args[1].As<Function>();
...
NanReturnUndefined();
}
void init(Handle<Object> exports)
{
NODE\_SET\_METHOD(exports,"parseMesh",ParseMesh);
}
NODE\_MODULE(meshtool, init);
以上代码最后一行定义模块名称meshtool,和加载它的时候调用的初始化方法init.
而初始化方法中则设置了输出的函数名parseMesh, 而实际接受parseMesh调用的C++方法即ParseMesh。
再来看这个ParseMesh方法,由于前面所说,因为v8::Argument的存在,所有JS的函数在C++层的方法参数和返回值都是一致的,所以它可以被一个NAN_METHOD的宏来处理,该宏根据Node版本将方法展开成对应的形式。保证ParseMesh方法可以在初始化中注册为任意版本JS函数parseMesh的的实现。
最后的NanReturnUndefined()表示该方法返回undefined (没有返回值即返回undefined). Nan还有很多其他的Return形式可以使用。
到此我们已经可以用C++ Addon来输出简单的JS函数了,这对于大多数情况已经够用。然而NodeJS还提供了一些更高大上的东西,比如输出一个自定义的JS的类型,或者在C++中使用多线程,并异步执行回调等。
进阶
进阶1: 输出一个JS包装类型
前一篇只提到了如何输出一个JS方法,但有的时候如果我们想输出的是一个C++的对象呢,这种情况在想要包装一个现有的C++库到JS的时候出现的尤其频繁。
如果我们仅有输出C++方法成为JS函数一条路,那也有笨办法,用C++代码表示:
//C++
class Body
{
Body();
void Move();
};
NAN\_METHOD(CreateBody)
{
NanScope();
Body\* handle = new Body();
NanReturnValue(NanNew<Integer>(reinterpret\_cast<int>(handle)));
}
NAN\_METHOD(BodyMove)
{
//check arguments
Body\* handle = reinterpret\_cast<Body\*>(args[0].As<Integer>()->intValue());
handle->Move();
}
NAN\_METHOD(DestroyBody)
{
//check arguments
Body\* handle = reinterpret\_cast<Body\*>(args[0].As<Integer>()->intValue());
delete handle;
}
void init(Handle<Object> exports) {
NODE\_SET\_METHOD(exports,"createBody",CreateBody);
NODE\_SET\_METHOD(exports,"bodyMove",BodyMove);
NODE\_SET\_METHOD(exports,"destroyBody", DestroyBody);
}
NODE\_MODULE(native_body, init)
相应的使用native addon的JS代码:
var native=require("native\_body");
var handle = native.createBody();
native.bodyMove(handle);
native.bodyDestroy(handle);
其实就是将一个Body对象的指针作为JS的一个int变量让JS层持有,每当要操作该对象时,重新将该指针传回。
但是这样的实现有很多缺点:
网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。
需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)
一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!
其实就是将一个Body对象的指针作为JS的一个int变量让JS层持有,每当要操作该对象时,重新将该指针传回。
但是这样的实现有很多缺点:
**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**
**需要这份系统化的资料的朋友,可以添加V获取:vip204888 (备注大数据)**
[外链图片转存中...(img-Ah1p0y28-1713395438537)]
**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**