美文网首页
如何理解node的多进程

如何理解node的多进程

作者: jluemmmm | 来源:发表于2020-09-09 08:59 被阅读0次

    事件驱动

    nodejs基于chrome v8引擎构建,默认单线程单进程模式,nodejs的单线程指的是js引擎只有一个实例,且是在主线程执行的(减少线程间切换的开销,不用考虑锁和线程池问题),其他的异步IO和事件驱动相关的线程通过libuv实现内部的线程池和线程调度。libuv存在一个事件循环,事件循环中维持一个执行栈和任务队列,在执行栈中,如果有异步IO及定时器等函数的话,就把异步回调函数放入事件队列中,执行栈执行完成后,从事件队列中按照一定的顺序执行事件队列中的异步回调。

    模块调用

    nodejs中的模块调用,js代码调用c++核心模块,核心模块调用内建模块,通过libuv进行系统调用,送入线程池等待执行。线程池中的I/O操作调用完成之后会保存结果向IOCP(一种高性能的I/O模型,一种应用程序使用线程池处理异步I/O请求的机制)提交执行状态告知当前对象操作完成并将线程归还线程池

    非阻塞I/O

    程序执行过程中需要进行很多I/O操作,读写文件、输入输出、请求响应等,I/O操作较为费时,基于node的事件循环机制,在I/O操作的同时,可以继续执行其他的代码。如文件IO,nodejs调用libuv后,libuv在其他线程执行I/O任务,线程完成任务后会通知主线程,主线程回调nodejs。

    多进程

    nodejs以单线程模式运行,使用事件驱动处理并发,可以在多核CPU系统上创建多个子线程。进程分为master进程和worker进程,master进程负责调度和管理worker进程,worker进程负责具体的业务处理,在服务器层面,worker是一个服务进程,负责处理来自客户端的请求,多个worker相当于多个服务器,因此构成一个服务器群,master进程负责创建worker,接收客户端的请求,分配到各服务器上去处理,监控worker的运行状态及管理操作。

    node提供child_process模块创建子进程。nodejs实现多进程

    • spawn 使用指定的命令行参数创建新进程
    • fork 用于在子进程中运行的模块,基于spawn的封装,fork('./main.js)相当于spawn('node', ['./main.js']),fork会在父进程与子进程之间,建立一个用于通信的管道,用于进程间的通信
    • exec 使用子进程执行命令,缓存子进程的输出,将子进程的输出以回调函数参数的形式返回。execFile基于spawn封装,可以直接创建子进程进行文件操作,exec基于execFile封装,可以直接开启子进程执行命令,常见的应用场景如http-server以及webpack-dev-server命令行在启动本地服务是自动打开浏览器。

    浅谈NodeJS多进程服务架构基本原理

    集群与进程通信

    node主进程在创建子进程的时候吧把主进程正在监听的server当作参数传给子进程,子进程自己也创建一个server来监听主进程传来的server。通过process.on中的message接受信息,使用process.send发送信息。

    • ipc标准进程通信使用send方法发送消息时,第二个参数支持传入一个服务,必须是http服务或tcp服务,子进程通过message事件进行接收,回调的参数对应发送的参数,第一个参数为消息,第二个参数为服务,可以在子进程创建服务并对主进程的服务进行监听和操作
    const os = require("os"); // os 模块用于获取系统信息
    const http = require("http");
    const path = require("path");
    const { fork } = rquire("child_process");
    
    // 创建服务
    const server = createServer((res, req) => {
        res.end("hello");
    }).listen(3000);
    
    // 根据 CPU 个数创建子进程
    os.cpus().forEach(() => {
        fork("child_server.js", {
            cwd: path.join(__dirname);
        }).send("server", server);
    });
    
    /**-------------------子进程业务的代码----------------------*/
    const http = require("http");
    // 接收来自主进程发来的服务
    process.on("message", (data, server) => {
        http.createServer((req, res) => {
            res.end(`child${process.pid}`);
        }).listen(server); // 子进程共用主进程的服务
    });
    
    • 单个nodejs实例运行在单个线程中,为了充分利用多核系统,有时需要启用一组nodejs进程去处理负载任务,cluster模块可以创建共享服务器端口的子进程。cluster模块,通过isMaster属性,判断是否是Master进程,是则fork子进程,否则启动一个server,每个HTTP Server都能监听到同一个端口。cluster

    cluster 模块可以创建共享服务器端口的子进程。

    const cluster = require("cluster");
    const http = require("http");
    const os = require("os");
    
    // 判断当前执行的进程是否为主进程,为主进程则创建子进程,否则用子进程监听服务
    if (cluster.isMaster) {
        // 创建子进程
        os.cpus().forEach(() => cluster.fork());
    } else {
        // 创建并监听服务
        http.createServer((req, res) => {
            res.end(`child${process.pid}`);
        }).listen(3000);
    }
    

    模块分类

    • 核心模块:包含在nodejs源码中,被编译进nodejs可执行二进制文件的js模块,是lib和deps目录下的js文件,如http/fs等
    • 内建模块: 一般不直接调用,在native模块中调用,再require
    • 第三方模块:非nodejs源码自带的模块为第三方模块,如webpack/express,如lib目录下的fs.jsnative模块,而fs.js调用的src目录下的node_fs.cc是内建模块

    nodejs如何和libuv和v8一起合作

    nodejs深入系列文章

    Node.js运行原理、高并发性能测试对比及生态圈汇总

    相关文章

      网友评论

          本文标题:如何理解node的多进程

          本文链接:https://www.haomeiwen.com/subject/fansektx.html