编辑推荐

适读人群:《Node.js:来一打 C++ 扩展》读者群为有一定Node.js基础并且会一些C++,有过C++原生模块开发经验想系统学习或者并没有开发经验但是想入门的人。
  目前市面上的图书多停留在对Node.js入门知识的讲解,如果想进一步提升功力,你会发现无书可觅,无秘籍可得!现在,《Node.js:来一打C++扩展》来了,正好可以满足你的刚需——学习完《Node.js:来一打 C++ 扩展》,你可以毫无压力地秒变全端高手!打通任督二脉,笑傲江湖!
  《Node.js:来一打C++扩展》在深度上远远超过了目前市面上的Node书籍。《Node.js:来一打 C++ 扩展》自始至终围绕一个主题展开:从介绍Node.js的包和模块规范开始,深入解析(包括但不限于剖析Node.js自身的源码)Node.js的模块是如何在运行时被引入的,尤其是如何引入C++模块的;接下来详细讲解了在什么时候、为何要编写C++模块;借此契机,深入介绍了Node.js的基石ChromeV8和libuv,以及异步非阻塞的原理——不仅如此,《Node.js:来一打 C++ 扩展》更教你如何在底层去驾驭它们。所以,《Node.js:来一打 C++ 扩展》以Node.js的C++扩展为中心,衍生出对Node.js底层风光的层层剖析,最后再回归到如何编写Node.js的C++扩展,一气呵成。读来酣畅淋漓,痛快不已!
  买这一《Node.js:来一打 C++ 扩展》相当于买了Node.js的底层风格、C++扩展编写、ChromeV8和libuv三《Node.js:来一打 C++ 扩展》!
  读完《Node.js:来一打 C++ 扩展》后,你甚至能为Node.js自身的添砖加瓦做出非凡贡献。

内容简介

  Node.js作为近几年新兴的一种编程运行时,托V8引擎的福,在作为后端服务时有比较高的运行效率,在很多场景下对于我们的日常开发足够用了。不过,它还为开发者开了一个使用C++开发Node.js原生扩展的口子,让开发者进行项目开发时有了更多的选择。
  《Node.js:来一打C++扩展》以ChromeV8的知识作为基础,配合GYP的一些内容,将教会大家如何使用Node.js提供的一些API来编写其C++的原生扩展。此外,在后续的进阶章节中,还会介绍原生抽象NAN以及与异步相关的libuv知识,最后辅以几个实例来加深理解。不过,在学习《Node.js:来一打 C++ 扩展》内容之前,希望读者已经具备了初步的Node.js以及C++基础。
  阅读《Node.js:来一打C++扩展》,相当于同时学习ChromeV8开发、libuv开发以及Node.js的原生C++扩展开发知识,非常值得!

精彩书评

ThisbookcontainsabsolutelyeverythingyouneedtoknowabouthowallthepiecesofNode.js’C++codeworkandinteract,explainingthenecessaryconceptswithoutneedingpriorknowledgeabouttheinternalsofV8,libuvorotherpiecesofNode.js.ItshowswellhowNode.js’ownbuiltinmodulesareconstructedusingtheAPIsprovidedbyV8,sothattheyareusablefromJavaScript,andhowyoucancreatethesamekindofmodulesfromscratch.Afterhavingreadthisbook,youwillbeabletowriteaproduction-quality,future-proofC++extensionforNode.jsifyouneedtodothat,ormaybeevenmakechangesNode.jsitselfifyou’reinterestedinthat!

Node.js:来一打 C++ 扩展》包含了所有你需要了解的有关Node.jsC++代码是如何运行和交互的知识,解释了一些你不需要知道V8的内部机制就能理解的必要概念,并介绍了libuv以及其他一些内容的方方面面。《Node.js:来一打 C++ 扩展》还展示了Node.js的内置模块是如何使用V8的API进行构建并可在JavaScript层面使用的——并且你也能用这种方法从头开始创建相同类型的模块。

读完《Node.js:来一打 C++ 扩展》,你将学到如何写出产品级质量的、面向未来的Node.jsC++扩展。感兴趣的话,你甚至可以对Node.js自身进行修改!

——安娜·亨宁森(AnnaHenningsen,addaleax),Node.js技术指导委员会成员(Node.jsTSC)

Node.js不是第个将JavaScript带入服务端领域的技术,然而它却成为史上热门、有影响力的工具之一。究其原因,其一,在于Node.js适逢后端高并发潮流,巧妙结合Reactor模型和JavaScript所擅长的回调风格,大大降低了开发高并发服务器应用的成本;IVNode.js:来一打C++扩展

其二,在于恰逢浏览器大战,前端技术突飞猛进,急需一套适合JavaScript和前端工程师的生态和工具链,Node.js刚好成为前端JavaScript易上手掌握的命令行环境。在Node.js发展得如此火热之际,Node.js的开发体验在不断提升,上手门槛也在不断降低。

然而,如果大家真正想突破自己并成为个中高手,无论是后端程序员希望在服务端及架构方面有所建树,还是前端程序员想跨越边界,都应该去了解Node.js的底层机制,去学习写一些Node.js的扩展。从Node.js的内在机制,我们可以学到更多有关计算机体系的知识,如内存管理、多线程编程等,真正向一个架构师、一个大牛迈进。

Node.js:来一打 C++ 扩展》在这些方面提供了一个非常系统的指南。死月通过精彩的内容告诉大家:底层的知识并不枯燥,用C++写一个扩展很有意思也很简单。作为Node.js工程师/爱好者的你,值得拥有《Node.js:来一打 C++ 扩展》。

——曹力(ShiningRay),酷链科技CEO,暴走漫画前CTO,糗事百科前联合创始人,高级Node.js技术专家,《JavaScript高级程序设计》译者

NativemoduleisoneofthemostunderappreciatedfeaturesofNode.js.Butevenintheageofasm.jsandWebAssembly,itisanirreplaceablepartoftheNode.jsecosystemduetoitsversatilityandperformance.XadillaX’sbookprovidesarefreshingintroduction(oreintroduction),andisamust-readforalllow-levelNode.jsengineers.

原生模块是Node.js中被低估的功能之一。因为自身的性能和多样性,使其即使是在asm.js和WebAssembly时代,仍旧能作为Node.js生态系统中不可替代的部分存在。死月的书对其进行了一个令人耳目一新的介绍,它是所有底层(Low-Level)Node.js工程师的必读之物。

——顾天骋(TimothyGu),pug、ejs前Maintainer,Node.jsCoreCollaborator之一

Node.js:来一打 C++ 扩展》全面讲解了V8、libuv的原理并且手把手教你编写一打Node.js的C++扩展,它是目前市面上相关领域非常稀缺的技术书籍。如果你想更深入地了解Node.js的实现原理,除了熟读内置API文档之外,阅读《Node.js:来一打 C++ 扩展》也会是一个很好的选择。

——雷宗民(老雷),《Node.js实战》作者之一

这是一本角度“刁钻”的Node.js相关书籍,其与市面上大多数Node.js书籍的定位不同。《Node.js:来一打 C++ 扩展》借为Node.js开发C++扩展做基石,顺带介绍了ChromeV8和libuv的内容,填补了市场上这一类书籍的空白,值得一读。

——李启雷博士,趣链科技CTO

无论是基础部分的V8练习,还是使用Node.js经典的Addon开发、用NAN来改写,或是libuv里的WatchDog案例、EFSW的封装,死月一直把实战贯穿在整《Node.js:来一打 C++ 扩展》之中。甚至在第8章里他还特意剖析了两个C++模块,把之前讲解的基础知识部分综合起来,以便让读者可以边学边练。

在如今追求大而全的时代,这本《Node.js:来一打C++扩展》单纯地讲Node.js的某一个方面,而且讲得特别棒,真的很难得。

——刘琥(响马),西祠胡同创始人,fibjs作者

当你掌握了Node.js的上层使用,下一步进阶的方向就是研究Node.js的底层原理。《Node.js:来一打 C++ 扩展》为学习Node.js的实现机制打开了一扇门。书中介绍的上下文(Context)、句柄(Handle)、句柄作用域(HandleScope)等概念直接来自源码,对于阅读Node.js及V8的源码具有极高的参考价值。

——潘旻琦(pmq20),Node.js技术专家,Node.jsCollaborator之一,RubyConf讲师之一

国内Node.js偏向于原理的书目前只有朴灵的《深入浅出Node.js》一本。至今4年过去了,Node.js的版本已经从v0.10发展到v9,中间几乎没有这样系统、有深度的书籍。

很高兴死月的新书弥补了这一遗憾。《Node.js:来一打 C++ 扩展》以C++为主线,涵盖Node.js核心的libuv和V8,对理解Node.js原理有极大的好处。当然好处在于,使用C++编写Node.jsAddon可以让Node.js有更广阔的应用空间。我们都知道Node.js擅长的是I/O密集型任务,对于CPU密集型运算这是极好的弥补。

特别推荐大家阅读此书,在Node.js应用极其广泛的今天,使用C++编写Node.jsAddon是更出彩的部分,你值得拥有。

——桑世龙(i5ting、狼叔),StuQ明星讲师,Node.js技术布道者,《更了不起的Node.js》作者

死月对Node.js底层机制有非常深入的了解。阅读《Node.js:来一打 C++ 扩展》,除了学习C++扩展开发外,还会跟随死月了解V8、libuv,相信大家对于Node.js的理解会更上一层楼。

——孙信宇(芋头),大搜车无线架构团队负责人,前端乱炖站长

C++扩展其实是从外在,用C++的角度去观察Node.js内在的形式。因为Node.js整个系统自身几乎就是构建在C/C++之上的,所以C++扩展只是在Node.js内部被称为内置模块(built-inmodule),在第三方角度(user-land)则被称为Addon,它们在本质上其实没有区别。死月凭借他在C/C++方面的深厚积累,选择从C++扩展作为突破口,带大家领略Node.js底层的风光。在《Node.js:来一打 C++ 扩展》中,你能看到真正发挥巨大价值的V8、libuv亦是精彩纷呈。

死月将C++扩展写得这么透彻,我是服的。

——田永强(朴灵),高级Node.js技术专家,《深入浅出Node.js》作者

开发C++扩展,可以扩充Node.js平台的本地API,扩充Node.js应用的能力。《Node.js:来一打 C++ 扩展》详细介绍了包括libuv、V8在内的各种必要知识,是该领域比较难得的好书。对C++开发者来说,《Node.js:来一打 C++ 扩展》既可以作为入门指引,又可以作为日常开发的有益参考。

——王文睿博士(RogerWang),node-webkit和NW.js项目创始人和维护者,英特尔软件架构师

我至今仍然清晰记得,自己手写的第个Node.jsC++扩展模块在Node.js0.6.9跑通的那种愉悦感。随着应用升级到Node.js0.8,依赖的C++扩展模块无法安装编译成功,最后发现是V8的API变化导致不兼容,从此我对C++扩展模块产生了抗拒心理。如今看到《Node.js:来一打C++扩展》从实现原理到V8基础概念的一系列介绍,让我重新对C++扩展模块产生了兴趣。参考《Node.js:来一打 C++ 扩展》中的实战例子,并在NAN的辅助下,编写一个跨Node.js版本的C++扩展已经不是什么困难的事情了。通过后一章,读者可以了解到Node.js官方的N-API计划,让C++扩展不仅仅能跨版本复用,还能跨操作系统(平台)复用。

——袁锋(fengmk2),Node.js技术专家

目录

1Node.js的C++扩展前驱知识储备1

1.1Node.js的模块机制2

1.1.1CommonJS的模块规范2

1.1.2Node.js的模块4

1.1.3小结9

1.1.4参考资料9

1.2Node.js的包机制9

1.2.1CommonJS的包规范9

1.2.2Node.js/NPM下的包13

1.2.3NPM与CNPM16

1.2.4小结19

1.2.5参考资料19

1.3Node.js依赖简介20

1.3.1ChromeV820

1.3.2libuv25

1.3.3其他依赖28

1.3.4小结30

1.3.5参考资料30

1.4C++扩展开发的准备工作31

1.4.1编辑器/IDE31

1.4.2node-gyp36

1.4.3其他构建工具54

1.4.4小结56

1.4.5参考资料56

2C++模块原理简析57

2.1为什么要写C++模块57

2.1.1C++比JavaScript解释器高效57

2.1.2已有的C++轮子72

2.1.3小结77

2.1.4参考资料77

2.2什么是C++扩展78

2.2.1C++模块本质78

2.2.2Node.js模块加载原理80

2.2.3小结102

2.2.4参考资料103

3ChromeV8基础104

3.1Node.js与ChromeV8104

3.2基本概念105

3.2.1内存机制105

3.2.2隔离实例(Isolate)108

3.2.3上下文(Context)109

3.2.4脚本(Script)110

3.2.5小结110

3.2.6参考资料111

3.3句柄(Handle)111

3.3.1本地句柄(Local)112

3.3.2持久句柄(Persistent)115

3.3.3永生句柄(Eternal)119

3.3.4待实本地句柄(MaybeLocal)119

3.3.5小结121

3.3.6参考资料121

3.4句柄作用域121

3.4.1一般句柄作用域(HandleScope)122

3.4.2可逃句柄作用域(EscapableHandleScope)125

3.4.3小结129

3.4.4参考资料129

3.5上下文(Context)129

3.6模板(Template)133

3.6.1函数模板(FunctionTemplate)133

3.6.2对象模板(ObjectTemplate)138

3.6.3对象模板的访问器(Accessor)与拦截器(Interceptor)144

3.6.4对象模板的内置字段(InternalField)175

3.6.5函数模板的继承(Inherit)183

3.6.6小结188

3.6.7参考资料189

3.7常用数据类型189

3.7.1基值(Value)189

3.7.2字符串(String)194

3.7.3数值类型196

3.7.4布尔类型(Boolean)196

3.7.5对象(Object)196

3.7.6函数(Function)200

3.7.7数组(Array)202

3.7.8JSON解析器203

3.7.9函数回调信息(FunctionCallbackInfo)203

3.7.10函数返回值(ReturnValue)204

3.7.11隔离实例(Isolate)204

3.7.12小结205

3.7.13参考资料206

3.8异常机制206

3.8.1try-catch206

3.8.2抛出异常209

3.8.3异常生成类(Exception)211

3.8.4小结211

3.8.5参考资料211

4C++扩展实战初探212

4.1binding.gyp212

4.1.1惊鸿一瞥213

4.1.2binding.gyp基础结构213

4.1.3GYP文件214

4.1.4常用字段221

4.1.5小结228

4.1.6参考资料228

4.2牛刀小试229

4.2.1又是HelloWorld229

4.2.2函数参数232

4.2.3回调函数234

4.2.4函数返回238

4.2.5小结239

4.2.6参考资料240

4.3循序渐进240

4.3.1C++与JavaScript类封装240

4.3.2实例化C++类封装对象的函数250

4.3.3将C++类封装对象传来传去253

4.3.4进程退出钩子255

4.3.5小结259

4.3.6参考资料259

5Node.js原生抽象——NAN260

5.1Node.js原生模块开发方式的变迁260

5.1.1以不变应万变260

5.1.2时代在召唤261

5.1.3小结267

5.1.4参考资料267

5.2基础开发267

5.2.1什么是NAN267

5.2.2安装和配置269

5.2.3先睹为快——搭上NAN的快车270

5.2.4基础帮助函数和宏276

5.2.5忽略node_modules279

5.2.6小结279

5.2.7参考资料280

5.3JavaScript函数280

5.3.1函数参数类型280

5.3.2函数声明282

5.3.3函数设置288

5.3.4小结296

5.3.5参考资料296

5.4常用帮助类与函数296

5.4.1句柄相关296

5.4.2创建数据对象298

5.4.3与数据对象“玩耍”300

5.4.4封装一个类314

5.4.5异常处理315

5.4.6小结315

5.4.7参考资料316

5.5NAN中的异步机制316

5.5.1Nan::AsyncQueueWorker316

5.5.2Nan::Callback317

5.5.3Nan::AsyncWorker317

5.5.4Nan::AsyncProgressWorker323

5.5.5小结327

5.5.6参考资料327

6异步之旅——libuv328

6.1基础概念329

6.1.1事件循环330

6.1.2句柄(Handle)与请求(Request)333

6.1.3尝尝甜头335

6.1.4小结340

6.1.5参考资料340

6.2libuv的跨线程编程基础341

6.2.1libuv的线程342

6.2.2同步原语(SynchronizationPrimitive)347

6.2.3工作队列355

6.2.4小结356

6.2.5参考资料357

6.3跨线程通信357

6.3.1uv_async_t句柄357

6.3.2Watchdog半成品实战解析358

6.3.3Watchdog试运行367

6.3.4小结368

6.3.5参考资料369

7实战——文件监视器370

7.1准备工作370

7.1.1功能规划370

7.1.2文件系统监听库——efsw373

7.1.3小结376

7.1.4参考资料376

7.2核心设计376

7.2.1API设计377

7.2.2EFSWCore的血肉之躯377

7.2.3EFSWCore的灵魂381

7.2.4小结385

7.3编写JavaScript类386

7.3.1类的设计386

7.3.2核心逻辑388

7.3.3简单容错391

7.3.4小结393

7.4进一步完善393

7.4.1C++代码的完善393

7.4.2JavaScript代码的完善398

7.4.3小结400

8实战——现有包剖析401

8.1字符串哈希模块——BlingHashes401

8.1.1文件设定402

8.1.2C++源码剖析403

8.1.3JavaScript源码剖析408

8.1.4小结409

8.1.5参考资料410

8.2类Proxy包——AutoObject410

8.2.1Proxy410

8.2.2AutoObject使用范例412

8.2.3代码剖析415

8.2.4小结424

8.2.5参考资料424

9N-API——下一代Node.jsC++扩展开发方式425

9.1浅尝辄止426

9.1.1实现一个Echo函数426

9.1.2尝试运行N-API扩展430

9.1.3向下兼容431

9.1.4N-APIPackage——C++封装433

9.1.5小结433

9.1.6参考资料433

9.2基本数据类型与错误处理433

9.2.1基本数据类型433

9.2.2与作用域及生命周期相关的数据类型435

9.2.3回调数据类型438

9.2.4错误处理439

9.2.5模块注册441

9.2.6小结442

9.2.7参考资料442

9.3对象与函数442

9.3.1对象442

9.3.2函数448

9.3.3类的封装453

9.3.4小结455

9.3.5参考资料455

前言/序言

写这《Node.js:来一打 C++ 扩展》是我在2016年底许下的愿望,希望在2018年初完成一本技术专著。

我于2012年加入Node.js开发的大军,现在也有幸成为Node.js这个项目的CoreCollaborator之一。所以,我的意向就是为大家呈现一本Node.js领域相关的书。但是现在市面上相关的书籍其实有很多了,我再写一本日常开发类的图书就显得有些多余。反而是在Node.js的C++扩展开发方面,无论是在国内还是在国外,都是一块死角。就目前而言,国外市场我也只看到过一本电子书,并没有纸质图书出版,国内就更没有了。

Node.js作为近几年新兴的一种编程运行时,托ChromeV8引擎的福,在作为后端服务时有比较高的运行效率,在很多场景下对于我们的日常开发已经足够用了。不过,它也跟“PHP提供了C语言开发其原生扩展的方式”类似,为开发者开了一个使用C++开发Node.js原生扩展的口子,让开发者进行项目开发时有了更多的选择。

实际上,在Node.js的生态圈中,就有很多使用C++完成的包。如最近比较火的深度学习TensorFlow,其Node.js版本的封装就是基于官方的C++源码完成的。我自己就是在日常开发中有一些相应的需求,使用纯粹的Node.js来开发可能会使开发成本有点大,或者基本上做不到,又或者有性能上的要求。这时,我就会选择使用C++来实现它的一个扩展。在我写了一段时间C++扩展之后,想到可能在社区中有很多像我一样的人,苦于Node.js在底层操作时的一些局限性,如果他们也加入C++原生扩展开发阵营的话,兴许要再踩一遍我以前踩过的“坑”,找我之前找过的资料。因此,我就想把自己一路走来的经验分享给大家,让更多的人顺利地加入Node.js的C++原生扩展开发的大军中。

我的Node.js之路

我个人从小学开始接触静态网页的开发,直到高中开始参加信息学奥林匹克竞赛(OlympiadinInformatics,OI),才算正式踏入了编程之路。

在大学的时候我仍旧坚持参加大学生程序设计竞赛(ACMInternationalCollegiateProgrammingContest,ICPC),并且一直使用C++和PHP进行开发。也是那时我打下了C++基础,这样才有机会现在完成这《Node.js:来一打 C++ 扩展》的写作工作。

我接触Node.js其实并没有国内一些早期的布道者们早,相反还是有点迟的。在2012年底,我决心学习Node.js,从而完成自己的一个创业项目。我当时的学习方法特别简单,买了一本BYVoid的《Node.js开发指南》,就算正式踏入了Node.js领域。

在熟悉了Node.js之后,我开始为Node.js生态圈造轮子,如Toshihiko1、ThmclrX、Huaming3、mcnbt4等。其实我个人认为,造轮子与写业务的一个不同点在于,造轮子可能会更容易遇到语言或是Node.js运行时本身的“坑”。所以,这就促使我去深究Node.js的文档,甚至源码。托我之前习得的C++基础的福,在阅读Node.js源码时并不觉得特别艰难。

我在老东家花瓣网的时候,就已经初步开始了Node.js的C++扩展开发。

后来我去了上一家就职的公司——大搜车,负责公司Node.js团队的建设。当时我就开始更深入地挖掘Node.js的一些内容了。甚至在2017年年中的时候,我通过给Node.js贡献源码,成为Node.jsCoreCollaborator之一。我在为Node.js贡献源码的时候,也为《Node.js:来一打 C++ 扩展》第3章和第6章的写作打下了基础。

原生扩展的一些示例

在Node.js早期的版本中,运行子进程是纯异步的,并没有像现在一样的各种spawnSync()等函数。我当时写了一个命令行工具,在其所用到的一个帮助类中实现一个参数校验的函数必须同步返回一个布尔类型的值;然而我在这个校验函数中所需要做的事情就是判断当前系统的Git版本。也就是说,我要通过子进程启动$git-v,并得到它的结果看看版本是不是符合要求。当时Node.js中运行子进程是异步的,达不到我的要求,所以我自己使用C++封装了一个原生扩展,使其能在Node.js的事件循环中同步开启子进程并在其结束后获得它的终端输出——虽然Node.js天生异步,但是在我的一个命令行工具中用同步形式执行这些内容也是没有问题的。

再比如,在大搜车的时候,项目用了阿里云的消息队列服务,而这款产品当时只有闭源的Java、C++等SDK,而C++的SDK就只提供了几个动态链接库和一堆头文件,我们使用Node.js的开发者就完全没法使用其服务。如果一定要用Node.js进行开发,一个成本比较高的做法就是自己去逆向分析及研究消息队列服务的各种网络包的结构,自己解析,然后用Node.js实现一个同样功能的库。然而这个方法基本不可行——尤其是在我们的项目高速迭代的时候。那么另一个办法就是基于其闭源的C++SDK,使用《Node.js:来一打 C++ 扩展》中的各种开发方式,写一份Node.js的C++扩展。这样就能把它们的C++SDK集成到我们的Node.js项目中了。这是一个非常好的降低开发成本的方法。

当年我还在花瓣网的时候,有一个需求是提取一张图片的主题色。我当时翻阅了不少论文,最终采用了一种八叉树加最小差值法的结合体1来完成这个需求。在数据结构和整型数字处理方面,我个人认为C++的开发效率和执行效率比Node.js要高,于是我自然而然地就使用了C++把核心算法部分完成了(现在我甚至使用C语言又重构了一套,开源在GitHub上面2)。然后为了将其集成到我们的Node.js任务调度系统中,我又将其封装成了一个Node.js的C++的扩展。这样一来,主题色提取的任务就欢快地运行了——它也被开源在我的GitHub上面,就是前面提到过的ThmclrX。而且借这个包的“东风”,我的硕士毕业论文写的就是这么一套主题色提取的任务系统相关内容。

类似的案例还有很多。如计算字符串哈希值等,由于用JavaScript重写代码的时候,在整数的各种操作上会有很多“坑”,因此拿C++源码封装一下就非常简单了。甚至谷歌推出的CityHash这个算法只有一份冗长的C++源码,使用JavaScript重写的话将会是一个比较庞大的工作量;再比如解析MP4文件的时长,我个人不是多媒体相关领域的开发者,所以并不擅长。于是我弄了一份C++的源码,懒得转换——嘿,用C++扩展一包,直接就发布了;还有同步获取HTTPAPI的内容,写一个能继承的类似于ECMAScript6中Proxy特性的拦截器;等等。

Node.js:来一打 C++ 扩展》面向的读者

在阅读《Node.js:来一打 C++ 扩展》前,我希望你对Node.js比较熟悉,并且对于C++这门语言至少要有一个初步的认识。当然,如果你的C++基础并不是很好的话,也不要怕,可以多读几遍本节最后的一段话。

Node.js:来一打 C++ 扩展》不仅仅讲实践,我还花了不少篇幅来讲解它的前驱知识,如ChromeV8引擎开发的一些基本概念,如句柄、句柄作用域等,以及各种API的初步介绍。另外,书中还介绍了libuv层面的内容,尤其是在异步方面,像libuv中的线程、同步原语,以及如何在Node.js的主时间循环中与你自身写的线程进行跨线程通信等。这么一算,ChromeV8、libuv,加上Node.js的C++扩展开发,你相当于一下子买了3《Node.js:来一打 C++ 扩展》,是不是觉得很超值?也就是说,你阅读《Node.js:来一打 C++ 扩展》的目的不一定是想要开发Node.js的C++扩展;如果你想学习ChromeV8,或者想学习libuv,也可以参考《Node.js:来一打 C++ 扩展》。

Node.js:来一打 C++ 扩展》的最后还简单展望了一下Node.js8.0之后出现的一个新特性,就是新一代Node.jsC++原生扩展接口N-API。不过由于N-API还处于试验阶段,各种接口还不是很稳定,在未来随时会变,因此《Node.js:来一打 C++ 扩展》中并没有详细地介绍N-API,而只是简单讲解了它的思想,让大家在心中有一个思想准备。这样,哪一天N-API正式发布了,读者就可以比较快地上手了。

不过,不要忘本,哪怕N-API真的出来了,我还是希望大家多了解一下底层的基础,比如像ChromeV8、libuv以及Node.js源码相关的内容。因为学习了这些基础知识,对大家肯定没有坏处(甚至对于Node.js,大家说不定会有一个新的认识)。

最后,奉上我在一次技术直播中说过的一句话:“当我们在学习Node.js的时候,我们其实就是在学编程。语言只是最表象的东西,思想才是核心内容。”如果还有部分读者由于《Node.js:来一打 C++ 扩展》需要有C++基础望而却步的话,多读几遍我刚才说的话,然后鼓起勇气入“坑”吧。

Node.js:来一打 C++ 扩展》的结构

Node.js:来一打 C++ 扩展》共分为9章。其中前两章描述了一些基础的前驱理论知识;第3章到第6章讲的是Node.js的C++扩展开发中用到的各种知识,并辅以简单的样例;第7章和第8章为实战章节,根据现实需求来完成相应的Node.jsC++扩展;第9章为对未来的N-API的一个展望。

第1章讲述了我们在学习《Node.js:来一打 C++ 扩展》内容之前所需要了解的基础,如Node.js的模块机制与包机制,以及Node.js都是由什么三方依赖构成的。其中就提到了很重要的ChromeV8和libuv。本章的最后还讲述了要进行Node.js的C++扩展开发所需要做的准备工作,包括但不限于编辑器的挑选、开发环境的搭建等。

第2章主要讲述了什么是Node.js的C++扩展,它的本质是什么,并且什么情况下需要使用C++扩展,以及阐述了为什么在这些情况下要使用C++扩展。

第3章介绍了谷歌的ChromeV8引擎,从它与Node.js的关系讲到它的一些基本概念,例如V8的内存机制、基本对象等。在后续的章节中将开始介绍ChromeV8的各种类及其概念,以及它们的用法,如句柄、句柄作用域、模板和各种常用的数据类型等。

第4章相当于各种编程语言书籍中的“HelloWorld”,向读者介绍了binding.gyp这个重要的配置文件,以及GYP文件格式的基础,然后以几个最简单的例子向读者展示了Node.js的C++扩展最简单的一些代码,包括函数的参数、回调函数的用法、对象的返回、函数的返回等,以及如何将一个C++的类封装成Node.js中直接能用的类。

第5章为大家介绍了NAN(NativeAbstractionsforNode.js)这个非常实用的包,使大家能在不同的Node.js版本(本质上是各不兼容的ChromeV8版本)中使用同一份C++代码。

第6章讲解了如何使用libuv进行异步Node.js的C++扩展代码编程,首先介绍了libuv的一些基础概念,如句柄与请求等,然后讲述了如何使用libuv进行跨线程编程。

第7章就开始进入了实战环节。本章通过从零开始写一个基于C++的文件监视器扩展,讲述了要完成一个Node.js原生扩展的一些流程。本章所述的文件监视器源码地址在https://github.com/XadillaX/node-efsw。

第8章与第7章的实战不同,对两个现有的简单C++扩展包进行分析,从另一个角度剖析了一个Node.js的C++扩展包的源码。

第9章展望了如何使用Node.js的最新特性N-API进行原生扩展的开发。不过我估计等到《Node.js:来一打 C++ 扩展》正式上市的时候,第9章已经变成一个仅供参考的章节了。

阅读《Node.js:来一打 C++ 扩展》的注意事项

声明:我在编写《Node.js:来一打 C++ 扩展》之际,还在大搜车工作,所以书中的很多内容都是基于大搜车的角度来写的。比如8.2节中有一处内容是这样的:

在笔者所在公司的内部,用了一套基于Dubbo深度定制的RPC服务框架。Node.js要访问这些Java服务的RPC函数是通过定制的HTTP协议来完成的,所有的RPC服务节点都到Zookeeper进行注册。

这里指的公司就是大搜车。再比如2.1.2节中的一段话:

在官方的Node.js版本ONSSDK出来之前,笔者自己造了一个基于其官方C++版本的ONSSDK封装的轮子,用的当然是《Node.js:来一打 C++ 扩展》所讲的姿势——Node.js的C++扩展了。

由于编写《Node.js:来一打 C++ 扩展》时我还并未从大搜车离职,因此这仍然是站在就职于大搜车的角度写的。我在编写《Node.js:来一打 C++ 扩展》之际,Node.js的8.x版本并未进入LTS1阶段。于是我采用了Node.js6.x作为样例进行了讲解,而Node.js6.x距离LTS结束也还有一段时间。而且使用《Node.js:来一打 C++ 扩展》的方法进行Node.js的原生扩展开发,在Node.js6.x、Node.js8.x甚至是Node.js9.x下都是通用的。

Node.js:来一打 C++ 扩展》中的样例都是基于Node.jsv6.9.4进行讲解的,读者在参考的时候上调或者下调几个中、小版本号问题都不大。

至于N-API一章(第9章),我在该章中也曾谈道:

本章内容在书中将会一带而过,因为在笔者写书的时候,N-API还没有完全稳定下来,随时会改变。而且笔者个人认为,距离N-API能正式投入生产用途的时间还很长。所以本章内容在《Node.js:来一打 C++ 扩展》中仅以扩展阅读的形式存在,其实关于N-API的内容在5.1.2节中曾略微提及。

因此,该章内容仅供参考,具体内容应以官方文档为准。

另外,《Node.js:来一打 C++ 扩展》所有的随书代码均在macOS命令行下测试通过。理论上,它们也可以在Windows和UNIX上运行良好,但我并没有验证过。

最后,给出《Node.js:来一打 C++ 扩展》中经常用到的一些地址。

?《Node.js:来一打 C++ 扩展》随书代码的Git仓库:https://github.com/XadillaX/nyaa-nodejs-demo

?Node.jsv6.9.4代码仓库:https://github.com/nodejs/node/tree/v6.9.4

?Node.jsv6.x所对应的ChromeV8文档:https://v8docs.nodesource.com/node-6.12/。若读者打开该地址,却发现页面不存在,可直接前往https://v8docs.nodesource.com/,

并点击“6.x”字样的超链接进入(注意该地址经常换)。

?作者的个人技术博客:https://xcoder.in

?作者的GitHub地址:https://github.com/XadillaX

?Me:https://github.com/XadillaX/me

致谢

感谢我的妻子,她也是一位优秀的Node.js研发工程师。她的支持是对我的最大鼓励,如果不是她,这《Node.js:来一打 C++ 扩展》的问世也许会更晚。

感谢我的父母,在我的背后默默地支持我的事业。在我很小的时候,他们就一直支持我追寻自己的梦想,这才使我能够在编程领域一路走下来。

感谢我的老东家大搜车,它营造了良好的技术与实践氛围,同事(包括领导)给予了我不少帮助,如书中图示的优化、阅读体验的建议等。这些同事有段鹏飞1、纪清华、刘佳楠、许波、王琦、袁小山……

感谢现实以及社区中的朋友们在《Node.js:来一打 C++ 扩展》创作的时候进行试读和探讨,并提供了一些其他帮助,他们包括但不限于Akagi201、ADoyle、DavidCai、Hax(贺老)、贺星星、精子(jysperm)、孟德森、天然、五花肉、引证、张秋怡。

感谢为《Node.js:来一打 C++ 扩展》写序和推荐语的作者们:安娜·亨宁森(AnnaHenningsen)、曹力(ShiningRay)、顾天骋(TimothyGu)、桑世龙(狼叔)、雷宗民(老雷)、刘亚中(Yorkie)、迷渡(justjavac)、潘旻琦(pmq20)、田永强(朴灵)、袁锋(苏千)、孙信宇(芋头)、

王文睿博士、响马,你们一直是我们的楷模与学习对象。

感谢我的高中计算机老师兼NOIP集训教练王震老师,王震老师是我在编程路上的启蒙老师,没有他就没有今天会写代码的我;也感谢当时陪我坚持走这一条路到毕业的好队友jiecchen和MatRush;感谢我的大学ACM教练宣江华老师和一直为集训队默默付出的陈萌老师;还要感谢我的研究生导师李启雷博士传道受业。

感谢博文视点的刘皎女士以及她的团队,是他们的努力使《Node.js:来一打 C++ 扩展》最终能与广大读者见面,他们提出的专业意见给了我很多帮助。

最后,还要特别感谢董伟明(《PythonWeb开发实战》作者)。在阅读了他的一篇文章《写一本技术书籍》后,我才有了写作《Node.js:来一打 C++ 扩展》的想法,并最终付诸实施。

死月(朱凯迪)

2018年3月于杭州

序一

1995年BrendanEich花了10天时间开发出了一门脚本语言,用来弥补JavaApplet的不足,随后MarcAndreessen给它起名为Mocha。其最初的定位是,Java用于大型专业级开发,而Mocha则是给测试脚本编写人员、业余爱好者、设计师使用的。

1995年5月,Mocha被集成到了Netscape浏览器中,其不久后改名为LiveScript,当年年底网景公司和Sun公司达成协议并获得了Java商标的使用权,其正式更名为JavaScript。

有人说Sun公司的介入限制了BrendanEich的手脚。JavaScript除了某些语法和Java类似以外,骨子里却是完全不一样的东西。

也有人说正式改名为JavaScript才使得这门语言成为浏览器执行的唯一语言。

时至今日JavaScript已经不仅仅局限于为网页做特效了,而真正发展成为一门全功能的编程语言:

?2008年Chrome发布、V8发布;

?2009年Node.js发布;

?2010年NPM发布;

?2014年12月,多位核心Node.js开发者不满于Joyent对Node.js的管理制度,创建了io.js;

?2015年初Node.js基金会成立;

?2015年9月Node.js4.0发布,Node.js和io.js正式合并。

Node.js4.0版引入了ES6的语言特性和“长期支持版本”的发布周期。

如今Node.js社区已经成为最活跃的编程社区之一,而从NPM的包数量来看,其已经超越了Java的Manven、Ruby的gem、PHP的composer。VIIINode.js:来一打C++扩展但是Node.js仍有很多不足之处,Node.js的使用者绝大部分仅仅把Node.js作为前端开发的辅助工具。大家把Node.js作为后端主力开发平台使用时,遇到CPU密集的场景时又不得不借助Java或者Go。虽然V8引擎一直致力于让JavaScript运行得更快,但是和Java、C++相比,还有不小的性能差距。

虽然关于JavaScript的书已经汗牛充栋,但是有关Node.js原理的书却屈指可数。而目前真正能够深入介绍原理的,国内的图书中也只有朴灵的《深入浅出Node.js》了,但如今四五年过去了依然没有等到该书的第2版,而死月的这《Node.js:来一打 C++ 扩展》却可以弥补这一方面的不足。

所有的编程语言底层都会回归C/C++,Node.js的底层依赖库V8使用C++开发,libuv则使用C语言。而使用C++开发Node.js扩展将直接把擅长CPU的C++和擅长I/O的Node.js结合在了一起,弥补了JavaScript在计算密集型应用方面的不足。

我从2015年开始研究V8,认识死月的时间则更早。死月不仅仅精通C++,他也是国内的Node.js布道师之一。从我认识他起,他就一直在使用Node.js。如果你想深入了解Node.js的原理,或者想打开Node.js另一个世界的大门,这本《Node.js:来一打C++扩展》值得你精读。

——迷渡(justjavac),Flarum中文社区创始人,国内知名前端技术专家

2018年3月22日于天津

序二

我跟死月相识于GitHub,那时我们经常会向Node.js贡献一些代码,彼此也会在微信上讨论一些技术问题。当我听说死月在写一本关于Node.jsC++扩展相关的图书时,激动得几乎要从床上蹦起来。因为我深知一个对Node.js与V8引擎都如此了解之人,愿意将他所知所想分享出来,这将是给予社区的一份大礼。

从我个人的角度来看,这《Node.js:来一打 C++ 扩展》非常适合这类开发者:他们对于Node.js的使用已经了然于胸,但却苦于没有底层开发经验,对整个V8虚拟机也一知半解。这时,他们可以从第3章开始读起。《Node.js:来一打 C++ 扩展》用了很长的篇幅介绍JavaScript代码究竟在虚拟机里是怎么运行的,它们又都分别对应着哪一类数据结构等。因为作者深知,只有把这些基础理解透了,则无论是开发C++扩展,还是写纯JavaScript代码,大家都能更得心应手。

Node.js:来一打 C++ 扩展》像是在述说着Node.js在C++扩展这一课题中曲折而又有趣的历史进程。首先从最原始的V8API时代开始。对于每个原始时代,开发者最痛苦的莫过于解决各种版本的兼容问题。之后迎来的是NAN时代。它解决了原始时代的接口抽象问题,接口也更丰富多样,异步接口也封装在内。最后,是还在路上的N-API。它与NAN一脉相承,拥有更官方的支持和更友好的接口。

另外,我们通常在写一个C++扩展时,多数情况下会跟异步打交道,这其中包含着如何非阻塞地调用底层接口,如何将异步的结果返回到JavaScript的回调函数中,以及如何正确地在异步封装中释放你的资源。对这些内容特别感兴趣的读者,可以打开第6章一睹为快。

Node.js已快走完它的第一个10年,尽管被人诟病于其回调地狱、虚假繁荣、超高并发场景下的不适应性,以及低端设备上的内存等问题,但这仍旧无法阻止它前进的步伐。然而对于我们Node.js工程师来说,除了掌握好这门语言之外,学习如何写C++扩展、了解它如XNode.js:来一打C++扩展

何运转将是我们下一阶段的重要功课。相信《Node.js:来一打C++扩展》将会成为常伴大家左右的另一本《代码大全》。

——刘亚中(Yorkie),Rokid系统工程师,tensorflow-nodejs作者

2018年3月22日于杭州


其他推荐