Open Genluo opened 5 years ago
指针:永远是指向一个地方,但是指针和指向的实例并没有什么联系,如果指针指向的实例如果搬走了,那么指针将指向一个空的地址,甚至是一个非法的地址 句柄:有一个传送门(垃圾收集器)记录这个对象的地址,如果有一天传送中心让这个对象换到其他的地方,并更新了所有指向这个对象的传送门,那么这样你通过句柄再访问这个对象的时候,那么还是这个对象
当一个对象没有句柄的引用时,那么这个对象将会被垃圾收集器回收
在 V8 中,内存分配都是在 V8 的 Heap 中进行分配的,JavaScript 的值和对象也都存放在 V8 的 Heap 中。这个 Heap 由 V8 独立的去维护,失去引 用的对象将会被 V8 的 GC 掉并可以重新分配给其他对象。而 Handle 即是对 Heap 中对象的引用。V8 为了对内存分配进行管理,GC 需要对 V8 中的 所有对象进行跟踪,而对象都是用 Handle 方式引用的,所以 GC 需要对 Handle 进行管理,这样 GC 就能知道 Heap 中一个对象的引用情况,当一个对象的 Handle 引用发生改变的时候,GC 即可对该对象进行回收或者移动。因此,V8 编程中必须使用 Handle 去引用一个对象,而不是直接通过 C ++ 的方式去获取对象的引用,直接通过 C++ 的方式去引用一个对象,会使得该对象无法被 V8 管理。
Handle 分为 Local 和 Persistent 两种。 从字面上就能知道,Local 是局部的,它同时被 HandleScope 进行管理。 persistent,类似与全局的,不受 HandleScope 的管理,其作用域可以延伸到不同的函数,而 Local 是局部的,作用域比较小。 Persistent Handle 对象需要 Persistent::New, Persistent::Dispose 配对使用,类似于 C++ 中 new 和 delete。 Persistent::MakeWeak 可以用来弱化一个 Persistent Handle,如果一个对象的唯一引用 Handle 是一个 Persistent,则可以使用 MakeWeak 方法来弱化该引用,该方法可以触发 GC 对被引用对象的回收。
简单来讲:开启多进程时候端口疑问讲解:如果多个Node进程监听同一个端口时会出现 Error:listen EADDRIUNS的错误,而cluster模块为什么可以让多个子进程监听同一个端口呢?原因是master进程内部启动了一个TCP服务器,而真正监听端口的只有这个服务器,当来自前端的请求触发服务器的connection事件后,master会将对应的socket具柄发送给子进程。
文档部分
cluster集群
Worker类: 通过cluster.fork方法返回一个Worker类实例,主进程中可以通过cluster.workers来获取worker类实例,工作进程中可以通过cluster.worker获取当前worker类实例
事件
方法
disconnect():在一个工作进程中,调用此方法会关闭所有的server并等待这些的server的close事件执行,然后关闭IPC通道,在主进程中调用,会给工作进程发送一个内部消息,导致工作进程自身调用.disconnect(),处理网连接后关闭,会更新exitedAfterDisconnect和触发disconnect事件,只针对服务端连接,注意不要和process.disconnect搞混,
isConnected()
isDead():当工作进程被终止时(包括自动退出或者发送信号),这个方法返回true,否则返回false
kill([signal="SIGTERM"]):注意与process.kill进行区分
send():主进程调用这个方法会发送消息给具体的工作进程。还有一个等价的方法是
ChildProcess.send()
。工作进程调用这个方法会发送消息给主进程。还有一个等价方法是
process.send()
。属性
事件
disconnect
exit:任何一个工作进程关闭
fork:当新的工作进程被fork
listening
message
online:当新建一个工作进程后,工作进程应当响应一个online消息给主进程,当主进程收到online消息后粗发这个事件,fork和online事件的不同之处在于:前者是在主进程新建工作进程后触发,后者是在工作进程运行时触发
setup
方法
disconnect():在每个工作进程中调用disconnect()方法
fork():只能通过主进程调用
setupMaster()
属性
isWorker
isMaster
schedulingPolicy:调度策略
settiings
workers:一个hash表,存储活跃的工作进程对象
1、集群cluster工作原理?
工作进程是由child_process.fork()方法创建,因此他们可以使用IPC和父进程通信,从而使各进程交替处理连接服务,也就是常见的主从模型
Node中处理多进程有这两种方案:
普通Node进程和cluster作业进程差异的情况有三种:
server.listen({fd: 7})
由于文件描述符“7”是传递给父进程的,这个文件被监听后,将文件句柄(handle)传递给工作进程,而不是文件描述符“7”本身。server.listen(handle)
明确监听句柄,会导致工作进程直接使用该句柄,而不是和父进程通信。server.listen(0)
正常情况下,这种调用会导致server在随机端口上监听。但在cluster模式中,所有工作进程每次调用listen(0)
时会收到相同的“随机”端口。实质上,这种端口只在第一次分配时随机,之后就变得可预料。如果要使用独立端口的话,应该根据工作进程的ID来生成端口号。由于各工作进程是独立的进程,它们可以根据需要随时关闭或重新生成,而不影响其他进程的正常运行。只要有存活的工作进程,服务器就可以继续处理连接。如果没有存活的工作进程,现有连接会丢失,新的连接也会被拒绝。Node.js不会自动管理工作进程的数量,而应该由具体的应用根据实际需要来管理进程池。
2、什么是"惊群现象"?
最初的Nodejs多进程模型是这样实现的:master进程创建socket,绑定到某个地址以及端口后,自身不调用listen来监听连接以及accept连接,而是将该socket的fd传递到fork出来的worker进程,worker接收到fd后在调用listen,accept新的连接,但是一个新的连接到来最终只能被一个worker进程accept再做处理,至于是哪个worker能够accept到,开发者完全无法预知以及干预,这势必造成一个新连接到来时,多个worker进程产生竞争,最终由胜出的worker获取连接
例如下面这种情况的产生:
2、为什么使用cluster可以使得多进程监听同一个端口,这是如何实现多进程共享端口的?
net.js源码中的listen方法通过listenInCluster方法来区分是父进程还是子进程,子进程的server拿到的是围绕的TCPWrapper,当调用listen方法时并不会执行任何操作,所以在子进程中调用listen方法并不会绑定端口,因而也并不会报错。也就是说在worker进程中调用listen不会执行任何操作,也就是cluster是个集大成者,整个模块的改动为了适应多进程的cluster的处理。
3、 cluster的负载均衡策略
在v6.0版本之前,cluster的调用策略采用的是cluster.SCHED_NONE(依赖于操作系统),SCHED_NODE理论上来说性能最好(Ferando Micalli写过一篇Node.js 6.0版本的cluster和iptables以及nginx性能对比的文章,点此访问)但是从实际角度发现,在请求调度方面会出现不太均匀的情况(可能出现8个子进程中的其中2到3个处理了70%的连接请求)。因此在6.0版本中Node.js增加了cluster.SCHED_RR(round-robin:时间片轮转法),目前已成为默认的调度策略(除了windows环境),可以通过设置cluster的schedulingPolicy属性来更改使用的负载均衡策略
和具体的操作系统有关
node内部维护两个队列,一个free队列记录当前可用的worker,另一个handles队列记录需要处理的TCP请求,当新的请求到达的时候,父进程将请求暂存handles队列,从free队列中出队一个worker,进入worker处理(handoff)阶段
worker处理阶段首先从handles队列出队一个请求,然后通过进程通信的方式通知自worker进行请求处理,当worker接收到通信消息后发送ack信息,继续响应handles队列中请求任务,当worker无法接受请求时,父进程负责重新地调度worker进行处理
通过给各个服务器分配一定的权重,每次选出权重最大的,给其权重减 1,直到权重全部为 0 后,按照此时生成的序列轮询,参考资料,
4、进程通信
matert通过fork创建子进程,他们之间通过IPC内部进程通信通道实现通信,操作系统的进程间通信方式主要有这几种:
5、node中的如何建立IPC通道
http://taobaofed.org/blog/2015/11/10/nodejs-cluster-2/
Node 中IPC支持
6、如果进程之间没有父子关系,如何实现任意进程之间的通信嘞?
参考文档
7、master、worker内部通信细节
开发过程中我们通常使用
process.on("message", fn)
来实现进程间通信,那么我们提到master
进程和worker进程在server
实例创建过程中,也是通过IPC通道进行通信的。那么为什么不会对我么的开发造成影响:当发送的消息包含cmd字段的,并且该字段以
NODE_
作为前缀,那这个消息会被视为内部保留消息,不会通过message
事件抛出,但可以通过监听internalMessage
进行捕获8、代码片段
(1) 超时直接kill子进程
8、本地进程通信方式
参考