为什么要读Nodejs源码?

本文转载自微信公众号「编程杂技」,作者theanarkh。转载本文请联系编程杂技公众号。

前几天有个同学和我说,他在看nodejs源码,但是不知道为什么需要看,也不知道从中可以学到什么。所以今天想聊一下关于阅读nodejs源码的意义。阅读其他源码也类似。

首先,阅读源码的目的无非两个

  • 1 深入了解和理解他或一些底层的原理
  • 2 从中学到一些优秀的设计思想和实践。

如果你没有两个目的,那其实就没有必要去看源码了。那接下来聊一下从阅读nodejs源码中,可以得到什么。

首先我们要深刻理解到nodejs是什么?大家都知道nodejs是一个js的运行时。那么到底nodejs里面有什么呢?每一部分的意义是什么呢?首先nodejs的组成是libuv、v8、第三方库。nodejs的定位首先是一个服务器,所以其实有libuv就可以了。那么剩余的组成部分有什么用?第三方库的作用很明显,就是复用了业界已有的解决方案去拓展了nodejs的一些功能,并不是nodejs的核心。v8的意义是因为nodejs选用了js这个语言,所以就需要一个js的引擎。否则v8也是不需要的,直接把libuv和第三方库编译成二进制就行,类似nginx,redis一样。这个是高层的视图。从底层来看,libuv是对操作系统功能的封装,v8是一个js解释器。那么看nodejs源码的意义就很明显了。nodejs的源码从垂直分为以下三个部分

  • 1 js层
  • 2 c++层(使用v8桥接js和libuv和一些自定义的c++逻辑)
  • 3 c层

读js层,你可以了解到nodejs实现的一些上层的逻辑,虽然js层最后还是依赖底层,但是js层也有很多逻辑,看懂了js层,在使用nodejs的时候,也就能更加深刻地了解到你在做什么,nodejs在做什么。但是js层是远远不够的。因为他只是个壳子。

那么接下来,你就要去读c++层,读c++层的代码,更多的是在了解如何去使用v8。假设你以后想在你的其他项目中单独使用v8,那么nodejs的代码就是一个参考。那么我们了解v8的使用有什么意义呢?这个问题相当于我们使用一个语言,然后了解他的编译器/解释器有什么意义。很多时候,我们都不需要这样做,这完全取决于你的兴趣。如果你以后想使用v8,或者你想了解js实现的底层原理,又或者你想了解一个编译器/解释器是如何实现的。那么你就可以去学习v8。v8首先作为一个js引擎,他里面有一个编译器/解释器的通用逻辑,词法解析,语法解析,代码生成,代码优化等等。然后还包含了js语言本身的实现细节,比如一个js数组,一个js对象,在v8里是怎么实现的。最后,了解v8为我们暴露了什么接口,我们可以使用v8做什么。

最后就是读libuv,nodejs的重点是作为服务器,所以相对来说,读libuv的才是重点,我们都知道v8只是一个js引擎,他没有网络,dns、文件等能力。在前端,js的文件,网络等能力来源于宿主浏览器。在nodejs,这些能力就来源于libuv。这是nodejs为什么叫nodejs,而不叫v8。因为他不只是v8。他还实现了自己的一些功能。所以你也可以实现自己的功能,加上v8的能力,创造出一个新的服务器。言归正传,那么读libuv可以学到什么东西呢?libuv是实现服务器的核心。所以我们可以从libuv中学习到实现一个服务器用到的技术。从libuv官网中我们也可以知道,libuv包括了进程、线程、定时器、文件、tcp、udp、unix域、线程池、dns等等能力,使用到操作系统能力包括进程间通信(管道、unix域、eventfd)、线程池、事件驱动(epoll、select、poll、kqueue等)、inotify机制、文件操作等等,使用数据结构和算法有二叉堆、红黑树、队列等。

总的来说,阅读nodejs源码最直接的是理解nodejs的工作原理和nodejs的本质。如果你是一个nodejs的开发者,这无疑是一个很好的收获。其次,如果你有兴趣,你还可以了解到编译器,操作系统的知识。额外地,你也可以学习到nodejs中的一些设计思想,比如定时器的设计,从早期版本到后来的重构背景。又或者如果你来设计一个服务器,你怎么设计。

从理论上聊了一下阅读nodejs源码的一些看法,下面顺便聊一下我自己的看法和体会。我阅读nodejs源码的原因是非常直接的,因为我希望我成为一个优秀的nodejs工程师。我对v8和libuv本身并没有太大兴趣。虽然我一直是一个前端工程师,但是我也不会去读js引擎的源码。至于libuv,相关的异步io库非常多,而且说到服务器的设计,nginx、redis无疑是更值得读的。但是成为一个优秀的nodejs工程师,深入了解和深刻理解nodejs本身是非常必要的。相对来说,nodejs是非常原生的。很多时候我们觉得读nodejs源码没有意义是因为没有深度或广度地去使用nodejs,可能只是停留到框架层面,复制着业务的最佳实践,成功地避开了一些坑。当你遇到一些难题,却又难以解决,甚至业界也没有解决方案的时候,你就会深刻理解到阅读源码的意义。当然,这种时候可能不会很多(比如https://cnodejs.org/topic/600b9de15d04ac76cf2181a7和https://cnodejs.org/topic/6018f1b103d797fb8e66e71c#6019f31903d7976d1066e9d2等)。

从开始读nodejs源码到现在,我觉得这是一段非常艰难、快乐、深刻的经历。他让我不仅更了解和理解nodejs,也在更高层面地提高了我。虽然我一直在推广阅读nodejs源码、写了很多文章、也和其他同学进行了很多交流。但是并不说明非读源码不可,个人觉得,读源码是一种好的习惯,也是让你变得优秀的方式,但是因为读哪些源码,这个完全取决于个人的兴趣和选择。不要盲目地去读,要带有目的。对个人而言,我是非常感谢nodejs,也非常敬佩nodejs作者和贡献者,但是我觉得我在阅读nodejs源码中,更多的是了解了nodejs的原理,并没有学到太多我想学的东西,v8算一方面,而libuv的内容,我觉得看nginx和操作系统内核可能是更好的选择,但是libuv相比来说可能更轻量,更快了解一个异步框架的设计。

这里顺便提一下nodejs源码分享的事情,有些同学希望我讲得通俗一点,或者结合应用来讲。这里说一下我做分享的一些想法。首先,我是一个在nodejs源码中学习的人,而不是站在nodejs之上的人,这意味着我也在慢慢学习,并且因为时间关系,我没有办法像图解系列,码农翻身一样讲得那么好那么易懂。因为我的初衷其实是做个笔记,然后分享给有兴趣的人,然后大家一起来做这个事情。我不是大家的老师,这也不是我的初衷和目的。我几乎都是免费分享内容、免费答疑。但是却很少碰到真正的志同道合者,大家还希望我讲得通俗易懂这对我来说就未免太难了。我承认如何讲好技术,做一个好的分享是一个值得学习的事情,但是我觉得这不是我现阶段的目的。而且我觉得一个有上进心的程序员,自己多动一下手和大脑,这个要求并不过分。一人之力有限,希望大家明白。

最后,提前祝大家新年快乐!对nodejs感兴趣的同学也可以找我一起交流学习!