从源码分析Node的Cluster模块

前段时间,公司的洋彬哥老哥遇到一个问题,大概就是本机有个node的http服务器,但是每次请求这个服务器的端口返回的数据都报错,一看返回的数据根本不是http的报文格式,然后经过一番排查发现是另外一个服务器同时监听了http服务器的这个端口。这个时候洋彬老哥就很奇怪,为啥我这个端口明明使用了,却还是可以启动呢?这个时候我根据以前看libuv源码的经验解释了这个问题,因为uv__tcp_bind中,对socket会设置SO_REUSEADDR选项,使得端口可以复用,但是tcp中地址不能复用,因为那两个监听虽然是同一个端口,但是地址不同,所以可以同时存在。这个问题让我不禁想到了之前看一篇文章里有人留言说这个选项是cluster内部复用端口的原因,当时没有细细研究以为说的是SO_REUSEPORT也就没有细想,但是这次因为这个问题仔细看了下结果是设置的SO_REUSEADDR选项,这个选项虽然能复用端口,但是前提是每个ip地址不同,比如可以同时监听'0.0.0.0'和'192.168.0.

  • pagecao
9 min read

Node中Buffer的初始化及回收

node中的buffer相信大家都不会陌生,毕竟这个东西是node的核心之一,我们读写文件,网络请求都会用到它。不过,之前我虽然一直在用这个东西,却没关心过他的实现,只知道通过buffer分配的内存占用的不是v8的heap上的内存,存在于newSpace和oldSpace之外,所以可以用它来进行一些大段内存的操作,但是却从没关心过它是如何分配内存,又是什么时候被回收这些问题。在一次有幸跟我神交已久的一位老哥的交流中,提起了这个问题才意识到自己这一块上确实存在盲区,于是专程去node源码(v8.1.4)中去寻找了一番,也算是颇有所得,所以专门写一篇文章记录和分享一下。 buffer的初始化 首先,我们可以从lib/buffer.js中,我们可以通过Buffer函数的代码往下追溯,发现Buffer的生成都是通过new FastBuffer来生成的,而FastBuffer我们可以看到代码中是这样实现的: class FastBuffer extends

  • pagecao
4 min read

Node子进程async/await方法不正常执行的思考和解决

前段时间,我做了一个node模块node-multi-worker ,希望通过这个模块让node能够脱离单线程的限制,具体的使用可以看一下上面的链接。其思路就是注册任务后,分出子进程,然后在主进程需要执行任务时,向reactor子进程发送命令,而reactor收到命令后分配到worker子进程在执行完成后返回结果到主进程。这篇文章主要是为了跟大家分享一下我在开发过程中,遇到的一个问题,如何解决以及对相关知识的一个挖掘。 不执行的async/await 在第一次完成了该工程后,我做了一些简单的测试,比如在子进程执行的方法中做一些加减乘除或者字符运算,当然都是没问题的。而对于一些异步的情况,我通过bluebird的处理也能够处理,于是我开始尝试起了aysnc/await的情况,结果发现这个的执行只要遇到await,await后面的语句能够执行,但是在下面的语句就再也不能执行了。这个情况顿时让我摸不着了头脑,我一度以为是v8内核中对于这种子进程的情况不支持(确实v8对你fork出子进程的支持是有问题的,不过跟这个问题没关,具体在模块的Readme中提到了),于是看了v8内部对async/await的实现,并没有什么发现有跟子进程有什么关系,

  • pagecao
8 min read

Node中console.log的同步实现

console.log相信使用过js的朋友都不会陌生,对于我这种前端转过来的node开发者,用起这个函数更是毫不手软,使用它把需要的信息打印到标准输出,觉得就是1+1=2那么正常,但是有天在网上看到一个问题console.log到底是异步还是同步?我觉得很诧异,这还是个问题么?当然是同步啦。但是问题的答案出乎我的意料,上面告诉我是要分情况的,根据process.stdout的情况可能会出现异步的情况。我当时眉头一皱,才发现问题确实不是我想的那么简单,于是在Node的文档中发现了这一段提示: Writes may be synchronous depending on what the stream is connected to and

  • pagecao
6 min read

深入node stream

要说node最令人印象深刻的模块,第一肯定是events,而第二肯定就是stream模块了,今天这篇文章就来跟大家聊聊stream的实现,主要是以stream_readable为例来讲解,并以fs模块中的createReadStream为例来说明node内部是如何使用stream的。 Stream.Readable 从lib/stream.js中我们可以看到node的stream主要分为了四种,读流,写流以及读写兼具的全双工流,还有可以修改和变换数据的 Transform流 ,不过读流和写流的实现逻辑和思路在大架构上是类似的,至于双全工流我们可以通过文件lib/_stream_duplex.js看到他其实就是通过: function Duplex(options) { ... Readable.call(this, options); Writable.call(this, options); ... } 使函数拥有了读流和写流所有的方法和属性而成,

  • pagecao
8 min read

Node中异步和同步的实现

使用过node的朋友都知道,它最重要的也是最值得称道的就是使用了异步事件驱动的框架libuv,这个框架使得被称为玩具语言的JavaScript也在后端语言中占了一席之地(当然V8的高性能也是功不可没,而且libuv的代码非常优雅,很值得大家的学习。不过libuv整个框架很大,我们不可能只通过一篇文章就能了解到它所有的东西,所以我挑选了node中最简单fs模块同步读和异步读文件的过程讲解来对libuv的一个大概过程有所了解。 fs.readSync fs.readSync这个方法我相信没有人会陌生,在node中同步读取文件,不过再很多文章中都不推荐使用这个方法,因为会造成node单线程的阻塞,对于一些比较繁忙的node实例来说是非常不友好的,不过今天我们不讨论这些,只讨论其中的实现。实现我们在node工程的lib目录中找到fs.js可以看到它的代码: function(fd, buffer, offset, length, position) { if (length === 0) { return 0; } return

  • pagecao
17 min read

记一次node协程模块开发

记一次node协程模块开发 开始 早前通过swoole了解到了协程的概念,正值当时看到了JS的GeneratorFunction,于是激动的心颤抖的手,敲着代码往前走,就用js写下了一个协程模块node-coroutine-js.当然这个模块比较简单,基本就是利用node本身的能力实现的,其中为了避免主线的阻塞所以使用了setImmediate的方法来执行方法。后来在了解协程方面信息的时候,了解到了libco以后,又产生了通过libco的方式来做node协程模块,因为libco这个库真的太厉害了,我接下来为大家分析一下其中我用到的swap相关的核心逻辑。至于libco的loop,在node的libuv面前却显得不太出众。 libco核心逻辑 libco是通过一个stCoRoutine_t结构体来管理一个协程单元的,这个结构中主要包括了该协程单元的被交换时的寄存器值,以及stack的空间,以及协程执行的方法和传入的参数值,通过co_create方法来初始化,初始化的过程也不算复杂,有兴趣的可以自己了解一下。然后在初始化了stCoRoutine_t以后,通过co_resume方法将协程方法调用起来,其具体代码如下所示: void

  • pagecao
17 min read
V8内存分配过程浅谈

V8内存分配过程浅谈

前言 本文会通过V8中对String对象的内存分配开始分析,对中间出现的源码进行解读。对heap内存的新生代分配和老生代内存分配的过程解读。首先,我们来看一张流程图,该流程图给出整个分配过程中的前期流程图,其中省略了一些步骤,只给出了关键的步骤。 从String::NewFromUtf8开始 我们从String::NewFromUtf8这个函数开始,首先我们来看一下使用,从samples/hello-world.cc中我们可以看到 Local<String> source = String::NewFromUtf8(isolate, "'Hello' + ', World!'", NewStringType::kNormal)

  • pagecao
28 min read

Mycat下的数据库读写分离以及分库分表

读写分离 读写分离解决的问题 1.可以使读操作分布到多个服务器,实现对读密集型应用的优化,并且使对数据库的负载均衡更加简单。 2.对备份来说,复制是一项很有意义的技术补充,不过复制不能取代备份。 3.可以实现高可用性,在发生故障需要切换的时候,读机器可以更快的切换到数据使系统能够显著的缩短宕机时间。 4.也可以用来做一些功能的升级的时候拿来做测试。 读写分离的原理 那么复制是如何工作的呢,主要是通过以下三个步骤: 1.在主库把数据更改记录到二进制日志(俗称 bin-log)中。 2.备库将主库上的日志复制到自己的中继日志(relay-log)中。 3.备库读取中继日志中的时间,并将其重放到备库的数据库上。 流程如下图: 读写分离的数据库配置

  • pagecao
12 min read

Linux上编译V8

Linux上编译V8 好吧~~看到这篇文章你也许会问: 编译一个有详细文档的V8有什么难的,你还写个东西?当翻译么? 嗯。。。如果你真的这样认为,那我只能告诉你:朋友你太天真了。v8的文档是个梗好吧。。我都没想到google这么大的公司开源个项目,文档写得如此粗糙,你要是想通过V8的文档来做v8的编译或者embed开发,我只能说 兄弟,你会被坑得错过大年三十的。为什么呢?因为开发者光顾着升级代码,不更新文档了,文档里大量老旧错误信息,网上这方面的信息上也有很多问题。 好了,多的不说了。我直接贴出这个编译的过程,中间遇到的坑以及编译samples中hello-world.cc的方法: 步骤一 google 在v8这个的编译上面用了一个极其蛋疼的工具(推广需要)。以前可以直接通过git拉下来,然后通过

  • pagecao
9 min read
Mysql-高性能索引

Mysql-高性能索引

索引一种数据结构,其目的是为了更快的查询数据,在数据量很大的表中,建立良好的索引能够提升极大的性能。 磁盘io与预读 因为数据库存储数据量大,是不可能存储在内存中以供查询的,所以对于数据的查询必然会跟磁盘打交道,所以只有了解了磁盘io和预读的基本知识,我们才能真正的理解索引的原理。 磁盘读取数据靠的是机械运动,每次读取数据花费的时间可 以分为寻道时间、旋转延迟、传输时间三个部分,寻道时间指的是磁臂移动到指定磁道所需要的时间,主流磁盘一般在5ms以下;旋转延迟就是我们经常听说的磁 盘转速,比如一个磁盘7200转,表示每分钟能转7200次,也就是说1秒钟能转120次,旋转延迟就是1/120/2 = 4.17ms;传输时间指的是从磁盘读出或将数据写入磁盘的时间,一般在零点几毫秒,相对于前两个时间可以忽略不计。那么访问一次磁盘的时间,即一次磁盘 IO的时间约等于5+

  • pagecao
17 min read

Subscribe to Magicare