入坑
先贴一段代码,再熟悉不过,她默默的待在Node.js官方首页上已经不知多长时间,迎接着初入Node.js世界的程序员们,所有人都认识她,但并非所有人都了解她,甚至很多人都没有想过要去了解她。
var http = require('http'); |
% node example.js |
我也是很多认识但不了解她的人们的其中之一,为什么我会想要去了解她呢,其实事情是这样的。之前用PM2设置集群的时候很容易,一条命令就搞定了,当时感觉很神奇,于是便看了下官网上关于集群的文档,但是Sample代码实在非常怪异,完全不是正常的思路,便越发想了解一下Node.js的集群机制到底是如何实现的,看源码吧,遂入坑,卒!先看cluster.js
,然后看child_process.js
,最后看net.js
,为什么最后是net.js
,因为实在看不下去了,各种异步,东一榔头西一棒子,完全理不清头绪。总结一下失败原因,首先并不了解Node.js代码结构、实现方式和内部机制,不适应异步逻辑,一直以顺序的思路去看代码,导致很多地方看不明白。其次对JavaScript其实并不熟悉,尤其这种大型项目,一些高级特性的使用,所谓的对象和继承等等,相比自己之前写的那些业务代码,完全不属于一个次元。然后就是轻敌,单枪匹马杀入乱军之中,没有赵子龙那样的本事,不卒才怪。于是痛定思痛,还是从头开始学吧!
体系架构
Node.js主要分为四大部分,Node Standard Library,Node Bindings,V8,Libuv,架构图如下。
- Node Standard Library 是我们每天都在用的标准库,如
require('http')
,官方的API文档说的就是他。 - Node Bindings 是沟通上下层的桥梁,封装V8和Libuv的细节,向上层提供基础功能。
- V8 是Google开发的JavaScript引擎,提供JavaScript运行环境,可以说没有他就没有Node.js。
- Libuv 是专门为Node.js开发的一个封装库,提供跨平台的异步I/O能力。
代码结构
以下是代码的简易结构,已经囊括了Node.js的四大部分,对于入门来说已经足够了,并且本文分析的绝大部分代码都在lib和src下面。另外,本文是基于v0.12.7版本进行的代码分析,网上也有一些老版本的分析,好像完全说的不是一回事,由于一时没注意带来了很多困扰,特此说明。
node |
特别声明
后文只着重描述了看代码的思路,并没有进行过多的说明,一是表达能力有限,二是感觉任何的说明都显得苍白无力,所以光看文章不看代码是不行的!
下面以Linus Torvalds的一句名言来开启Node.js的源码之旅。
Talk is cheap, show me the code.
Let’s go!
起步停车
本来我刚开始分析的是第二句代码http.createServer(...).listen(...);
,因为这句最长,一看就是重点嘛,但是分析完之后才发现,在这之前Node.js还做了好多好多事情,这才只是冰山一角,还是需要从真正的起跑线开始。
% node example.js |
这句话有啥可分析的,一开始确实是这样想的,本来认为可以轻松越过的,谁知道刚起步就停了下来,真的没有那么简单,如下图所示。
首先声明这不是正规的时序图,只是为了更好的理解代码才画成了时序图的样子,来描述整个代码的调用过程。上图描述了从command line
到进入example.js
之前的程序调用过程,属于整个HelloWorld程序的起步阶段。要想了解进入主程序之前Node.js都干了什么,细读这部分代码就可以了,尤其是node.cc
、node.js
和module.js
。其中node_Contextify.cc
中有很多关于V8的调用,暂时不在本文的讨论范围,有兴趣的可以了解一下。
Node Bindings
其实上一节还留有一个疑点,上图右边第三个Script
是怎么来的,是如何与C++代码联系上的?接着看代码!
vm.js
中有如下调用,process.binding
干了什么?
var binding = process.binding('contextify'); |
node.cc
的SetupProcessObject
中有如下设置,将process.binding
与Binding
进行绑定,Binding
干了什么?
NODE_SET_METHOD(process, "binding", Binding); |
node.cc
的Binding
中有如下调用,对模块进行注册,nm_context_register_func
干了什么?
mod->nm_context_register_func(exports, unused, env->context(), mod->nm_priv); |
node.h
中对mod
的类型node_module
有如下定义,往下看!
struct node_module { |
node.h
中还有如下宏定义,接着往下看!
#define NODE_MODULE_CONTEXT_AWARE_X(modname, regfunc, priv, flags) \ |
node_contextify.cc
中有如下宏调用,终于看清楚了!结合前面几点,实际上就是把node_module
的nm_context_register_func
与node::InitContextify
进行了绑定。
NODE_MODULE_CONTEXT_AWARE_BUILTIN(contextify, node::InitContextify); |
兜了这么大一个圈子,省略去中间步骤,代码对应如下,Node.js就是如此完成了Node Bindings。
process.binding('contextify'); |
步入正轨一
说了这么多终于到第一句代码了,再不到就要放弃了,赶快来看看吧。
var http = require('http'); |
require
是怎么来的,为什么平白无故就能用呢,实际上都干了些什么?
module.js
的_compile
中有如下代码。
var self = this; |
Module
的require
有如下定义。
Module.prototype.require = function(path) { |
Module
的wrap
有如下定义。
Module.wrap = NativeModule.wrap; |
node.js
中NavtiveModule
有如下定义。
NativeModule.wrap = function(script) { |
不用多解释了,代码已经说明了一切。
步入正轨二
正餐开始,不过感觉前面的开胃菜似乎有点多……
http.createServer(function (req, res) { |
一如既往,看图说话。
- 首先了解一下HTTP Server的继承关系,有利于更好的理解代码。
- 然后就是HTTP Server的工作流程。
通过上图可以看出,绝大部分逻辑都在net.js
中,细读这部分代码可以更好的了解其工作原理。其中tcp_warp.cc
中有很多关于Libuv的调用,暂时不在本文的讨论范围,有兴趣的可以了解一下。
步入正轨三
最后一句了,挺住!
console.log('Server running at http://127.0.0.1:1337/'); |
console
和require
还不一样,不是以参数的形式传进来的,这就要说到global
对象了,Node.js的顶层对象。官方文档已经有了相关的说明,在这就不多做解释,重点看看他是怎么来的。
node.js
中有如下定义,这个this
到底是谁?
this.global = this; |
node.cc
中的LoadEnvironment
有如下定义,f
代表node.js
所形成的方法,Call
跟JavaScript中的Function.prototype.call
是一个意思,也就是说f
中的this
指向的就是global
。
Local<Object> global = env->context()->Global(); |
这样console
作为全局变量的身份也就真相大白了。
大功告成?
所有代码都分析完了,“Hello World”这两个字竟然还没有出现!?
这是段服务端程序,没有请求,哪来的应答!?
哎,你要是长这样该多好……
console.log('Hello World'); |
即使没完,也准备告一段落了。给有缘人一个探索的空间?No!No!No!累了,需要恢复元气!
如果确实非常想知道后事如何,那便在此留下一些线索,以供参考。
其实在看代码的过程中,Server响应请求的过程更加令人匪夷所思,卡了好久,还好找到了比较好的办法才算弄清楚,那就是看!日!志!其实看日志根本不算什么办法,地球人都知道,但是怎么让日志打出来,还真费了半天功夫,反正百度上是没找到。
- V8日志
% node --trace example.js |
- 源码Debug日志
% NODE_DEBUG=HTTP,STREAM,MODULE,NET node example.js |
关于V8的一些参数可以通过node --v8-options
查看,--trace
的作用是输出方法调用过程。源码Debug日志的原理可以查看util.js
的debuglog
方法。这些日志都比较长,最好输出到文件中以便反复查看。
感想
别总以为什么都知道,其实可能连最基本的都不知道!
知道的越多,就越觉得无知!
唯有学习!