认识node核心模块--全局对象及Cluster

2017-10-26  本文已影响59人  莫凡_Tcg

原文地址在我的博客,转载请注明出处,谢谢!

node 模块是node 完成强大功能的实现者。node 的核心模块包括events、fs、buffer、stream、cluster、http、net、一些操作OS和工具模块、全局对象等。本文将在node核心特性理解的基础上进一步深入探讨node核心模块的具体细节。本文主要探讨的模块有:全局对象global及其重要属性、多进程cluster、events重要类EventEmitter、流Stream、文件系统fs、网络http,还会介绍node框架express相关。

概述

本文先来介绍全局对象global及其重要属性、多进程cluster模块。

正文

node全局对象global

与浏览器对应的window一样,在node中global是全局对象,在全局作用域定义的任何变量都会保存为global的属性,称为全局变量。下面是global一些重要的属性:

node 子进程:child_process模块

一个进程只能利用一个CPU时间分片,为了高效利用多核CPU,node 提供了可以创建子进程(注意不是子线程)child_process模块,来帮助主进程高效利用多核CPU完成其他复杂的任务。之所以提供创建子进程而不是子线程的接口,是因为这让我们的程序状态单一,不用在意状态同步、死锁、上下文切换开销等等多线程编程中的头疼问题。这样以来一个进程只有一个线程。虽然单线程也会带来一些问题,如错误会引起整个应用退出等,但这都有了很好的解决方案。

创建子进程

node有三种创建子进程的接口:

父子进程通信

首先,这三种API都返回ChildProcess实例,因此都可以通过访问stdout属性来得到输出,exec/execFile 接口还可以在参数里绑定回调函数拿到子进程的stdout 。

const { exec, execFile, spawn, fork } = require("child_process")

// exec/execFile 接口既可以在参数里绑定回调函数拿到输出流,也可以利用返回的ChildProcess实例
const exec_process = exec("node child_process.js", {}, (err, stdout, stderr) => {
    if (err) {
        console.log(err)
    } else if(stdout) {
        console.log(stdout)
    } else {
        console.log(stderr)
    }
})
exec_process.stdout.on('data',(data) => console.log(`${data}`))

// spawn 接口没有回调函数,只能利用返回的ChildProcess实例绑定监听数据函数拿到子进程的输出
const spawn_process = spawn('node',['child_process'], {})
spawn_process.stdout.on('data', (data) => console.log(`${data}`))

//fork 也可以利用返回的ChildProcess实例,注意配置项silent要设为true
const fork_process = fork('child_process.js', [], {'silent': true})
fork_process.stdout.on('data' ,(data) => console.log(`${data}`))    

其次, fork返回的ChildProcess实例有一个额外的内置的通信通道IPC,它允许消息在父进程和子进程之间来回传递。

// child_process.js
process.on('message', (data) => {
  console.log(`message from Parent: ${data}`);
})
setTimeout(() => {
  process.send('send from child');
}, 2000)

// parent.js
const { fork } = require("child_process")
const p = fork(
  'child_process.js', // 需要执行的脚本路径
  [], // 传递的参数
  {}
)
p.on('message', data => {                      //监听子进程消息
  console.log(`message from child: ${data}`)
})
p.send('send from parent')                     //发送消息给子进程

集群:Cluster 模块

cluster模块是对child_process模块的进一步封装,专用于解决单进程NodeJS Web服务器无法充分利用多核CPU的问题。使用该模块可以简化多进程服务器程序的开发,让每个核上运行一个工作进程,并统一通过主进程监听端口和分发请求。 ——七天学会node.js

const cluster = require('cluster');
const http = require('http');
const numCPUs = require('os').cpus().length;
console.log(numCPUs)

if (cluster.isMaster) {
  console.log(`主进程 ${process.pid} 正在运行`);

  // 衍生工作进程。
  for (let i = 0; i < numCPUs; i++) {
    cluster.fork();      // 调用了 child_process.fork()方法创建工作进程
  }

  cluster.on('exit', (worker, code, signal) => {
    console.log(`工作进程 ${worker.process.pid} 已退出`);
  });
} else {
  // 工作进程可以共享任何 TCP 连接。
  // 在本例子中,共享的是一个 HTTP 服务器。
  http.createServer((req, res) => {
    res.writeHead(200);
    res.end('你好世界\n');
  }).listen(8000);

  console.log(`工作进程 ${process.pid} 已启动`);
}
console.log("WOW")

// 输出(Mac OS)
4
主进程 55570 正在运行
WOW
4
工作进程 55571 已启动
WOW
4
工作进程 55572 已启动
WOW
4
工作进程 55573 已启动
WOW
4
工作进程 55574 已启动
WOW

//可以看到 fork 是异步创建的,调用时请求创建进程并立即返回,系统创建好进程后会加入到事件队列,执行到事件就调用回调函数,这个回调函数包括执行一遍这个文件

cluster.fork()实际调用了child_process.fork(),因此建立了IPC通道与父进程通信。它会创建一个进程并返回cluster.worker实例。创建的每个进程之间都是独立的,一个进程的开启和关闭不影响其他进程。只要有存活的进程,服务器就可以继续处理连接。

主进程负责监听端口,接收新连接后会自动将连接循环(默认)分发给cluster.fork()创建的工作进程来帮忙处理,因此可以使用cluster模块来实现简单的负载均衡。

注意

上一篇 下一篇

猜你喜欢

热点阅读