首要仍是一如既往的打个广告。假如对前端八股文感兴趣,可以看这儿,假如对react源码感兴趣,可以看这儿。感兴趣的朋友别忘了star哦,总有你要的干货[狗头]。

大纲

  • Node单进程形式简介
  • Cluster多进程形式简介
  • 功能测验东西ab简介
  • Cluster怎么影响服务器功能
  • Cluster是否进程数量越多越好
  • PM2运用简介

Node单进程形式

默认状况下,node以单进程运转,一个进程实例只要一个主线程(即事情循环线程)。关于多核CPU的核算机来说,这样做功率很低,由于只要一个核在运转,其他核都在闲置。

const express = require('express')
const app = express();
app.get('/slow', (req, res) => {
    let startTime = Date.now();
    while(Date.now() - startTime < 10000){}
    res.send('slow request')
})
app.get('/fast', (req, res) => {
    res.send('fast request')
})
app.listen(3000)

当咱们履行node index.js时,就发动了一个进程实例。这便是一个node服务。这儿只要一个进程实例。一起也是咱们的一个运用。这个进程实例包括一个主线程(也称事情循环线程)以及N个线程池中的线程。

只要有恳求进入咱们的服务器,就会在咱们的事情循环线程中处理。一旦恳求进入咱们的服务器,事情循环线程对其进行处理,然后回来呼应成果。

Node Cluster形式之进程数量是否越多越好?

假如某个恳求处理比较耗时,很明显就会堵塞其他恳求。以上面的服务为例,咱们运用下面的代码顺次恳求/slow以及/fast接口:

const startTime = Date.now();
fetch('http://localhost:3000/slow').then(res => {
   console.log('slow耗时:', Date.now() - startTime) 
})
fetch('http://localhost:3000/fast').then(res => {
   console.log('fast耗时:', Date.now() - startTime) 
})

成果如下,很明显,/fast恳求被/slow恳求堵塞了。这是由于主线程是单线程的,由于先恳求的是slow,该恳求堵塞了10秒,在这10秒内,主线程没法处理其他恳求,导致fast恳求被堵塞了。

Node Cluster形式之进程数量是否越多越好?

Cluster集群形式

可以在运用中运用cluster形式敞开多个进程实例,其中包括一个主进程和若干个worker进程。主进程负责监督worker进程实例的健康。主进程本身并不实际履行任何运用程序代码,不负责处理传入的恳求或履行其他操作,比方从数据库中读取数据。相反,主进程负责监控每个worker进程实例的运转状况。主进程可以发动单个实例,也可以中止或许重启它们,还可以向它们发送数据。这些worker进程实例负责实际处理传入的恳求,比方访问数据库,处理身份验证等。worker进程实例之间选用进程间通讯交流消息,cluster模块内置一个负载均衡器,选用Round-robin算法协调各个worker进程之间的负载。比方下图所示都是咱们的运用程序正在运转的实例。这是运转在一台核算机上的多个实例。

Node Cluster形式之进程数量是否越多越好?

下面是cluster 形式的简单demo,这儿只要一个进程实例,意味着只要一个事情循环线程。

新建一个index.js文件,代码如下:

const cluster = require('cluster')
const express = require('express')
// 这个文件是否是在master形式履行的
if(cluster.isMaster){
    // 主进程实例,会触发index.js以child形式从头履行一次
    cluster.fork();
} else {
    // worker进程实例
    const app = express();
    const doWork = (duration) => {
        const start = Date.now();
        while(Date.now() - start < duration){}
    }
    app.get('/', (req, res) => {
        doWork(5000)
        res.send('Hi there')
    })
    app.get('/fast', (req, res) => {
        res.send('This was fast!')
    })
    app.listen(3000)
}

假如先恳求/,紧接着恳求/fast接口,如下图。会发现/fast恳求被堵塞了

Node Cluster形式之进程数量是否越多越好?

下面的代码敞开了2个进程实例,意味着有2个事情循环线程,也便是2个独立的服务。

const cluster = require('cluster')
const express = require('express')
console.log(cluster.isMaster)
if(cluster.isMaster){
    // 下面调用了2次cluster.fork函数,意味着当咱们在终端履行node index.js时,还会再额外履行2次index.js,
    // 只不过别的2次的cluster.isMaster设置为false
    cluster.fork();
    cluster.fork();
} else {
    const app = express();
    const doWork = (duration) => {
        const start = Date.now();
        while(Date.now() - start < duration){}
    }
    app.get('/', (req, res) => {
        doWork(5000)
        res.send('Hi there')
    })
    app.get('/fast', (req, res) => {
        res.send('This was fast!')
    })
    app.listen(3000)
}

如下图,可以发现/fast恳求不会被堵塞了。这是由于这儿咱们有两个进程,一个进程处理/恳求,另一个进程处理/fast恳求。

Node Cluster形式之进程数量是否越多越好?

功能测验东西简介

这儿咱们运用Mac自带的ab东西对功能进行测验。以下面的代码为例

const cluster = require('cluster')
const express = require('express')
console.log(cluster.isMaster)
if(cluster.isMaster){
    cluster.fork();
} else {
    const app = express();
    const doWork = (duration) => {
        const start = Date.now();
        while(Date.now() - start < duration){}
    }
    app.get('/', (req, res) => {
        doWork(5000)
        res.send('Hi there')
    })
    app.get('/fast', (req, res) => {
        res.send('This was fast!')
    })
    app.listen(3000)
}

发动服务后,运用ab进行测验:

ab -c 50 -n 500 localhost:3000/fast

-n 500表明总共建议500个恳求

-c 50表明并发50个恳求,这意味着要测验一起建议50个恳求,保证在任何给定时刻点始终有50个恳求在运转并等候处理。当然,后边50个恳求在外

Node Cluster形式之进程数量是否越多越好?

Requests per second表明服务器每秒处理的恳求数。 Time per request: 23.833ms表明平均每个恳求花费的时刻

下面表明的是恳求时刻的散布规模。比方

50% 19表明50%的恳求在19ms内得到处理或呼应。

100% 35表明至少有一个恳求需求35ms的时刻才干做出呼应。

Percentage of the requests served within a certain time (ms)
  50%     19
  66%     21
  75%     22
  80%     23
  90%     30
  95%     33
  98%     35
  99%     35
 100%     35 (longest request)

Cluster 怎么影响服务器功能

为了便利观察Cluster Mode敞开的进程数量对功能有什么影响,下面的比方将线程池巨细调整为1。

process.env.UV_THREADPOOL_SIZE = 1;
const cluster = require('cluster')
console.log(cluster.isMaster)
if (cluster.isMaster) {
    cluster.fork();
} else {
    const express = require('express')
    const crypto = require('crypto')
    const app = express();
    app.get('/', (req, res) => {
        let startTime = Date.now();
        console.log('承受恳求:', startTime)
        crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
            console.log('恳求耗时:', Date.now() - startTime)
            res.send('Hi there')
        })
    })
    app.get('/fast', (req, res) => {
        res.send('This was fast!')
    })
    app.listen(3000)
}

发动服务,运用ab测验单个恳求耗时:

ab -c 1 -n 1 localhost:3000/

成果如下,可以发现恳求耗时559毫秒左右

Node Cluster形式之进程数量是否越多越好?

有了基准值后,咱们添加恳求的数量以及并发数。这次总共建议三个恳求,并发数为2。

ab -c 2 -n 3 localhost:3000/

由于后边两个并发恳求几乎一起抵达的,但是咱们只要一个进程实例,然后这个进程实例的线程池只要一个线程,咱们的服务器一次只能处理一个pbkdf2函数的核算。

Node Cluster形式之进程数量是否越多越好?

在单进程的状况下,咱们的服务是没法很好的处理并发恳求的。咱们可以测验着添加咱们的进程数量,修改咱们的demo,这次咱们运用两个进程实例:

process.env.UV_THREADPOOL_SIZE = 1;
const cluster = require('cluster')
console.log(cluster.isMaster)
if (cluster.isMaster) {
    cluster.fork();
    cluster.fork();
} else {
    const express = require('express')
    const crypto = require('crypto')
    const app = express();
    app.get('/', (req, res) => {
        let startTime = Date.now();
        console.log('承受恳求:', startTime)
        crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
            console.log('恳求耗时:', Date.now() - startTime)
            res.send('Hi there')
        })
    })
    app.get('/fast', (req, res) => {
        res.send('This was fast!')
    })
    app.listen(3000)
}

相同的,咱们仍是建议总共3个恳求,并发数为2。同一时刻最多有2个恳求抵达咱们的服务器,得益于cluster的负载均衡才能,假定咱们第一个进程处理第一个恳求,第二个恳求抵达服务器时,cluser主线程发现第二个进程在闲暇状态,于是将第二个恳求转发给第二个进程处理。

从下面的成果可以看出,后边两个恳求显然是几乎并行处理的。

Node Cluster形式之进程数量是否越多越好?

可以看到,当咱们添加进程实例时,形似可以进步咱们服务器的功能。由于咱们可以一个进程处理一个恳求,从而进步咱们服务器的并发才能。

Cluster 是不是进程数量越多越好

从上面的demo可以看到,咱们添加cluster进程数量,可以进步咱们服务器的并发才能。那么,是不是进程实例越多越好呢?

为了验证进程实例越多,服务器功能是不是越好,咱们看下面的demo。这次咱们创建了6个子进程。

process.env.UV_THREADPOOL_SIZE = 1;
const cluster = require('cluster')
console.log(cluster.isMaster)
if (cluster.isMaster) {
    cluster.fork();
    cluster.fork();
    cluster.fork();
    cluster.fork();
    cluster.fork();
    cluster.fork();
} else {
    const express = require('express')
    const crypto = require('crypto')
    const app = express();
    app.get('/', (req, res) => {
        let startTime = Date.now();
        console.log('承受恳求:', startTime)
        crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
            console.log('恳求耗时:', Date.now() - startTime)
            res.send('Hi there')
        })
    })
    app.get('/fast', (req, res) => {
        res.send('This was fast!')
    })
    app.listen(3000)
}

下面咱们建议总共7个恳求,并发数量为6

ab -c 6 -n 7 localhost:3000/

成果如下:

Node Cluster形式之进程数量是否越多越好?

实际上,这依赖于核算机CPU的装备。比方我的核算机CPU有4个内核。这意味着我的核算机处理传入恳求的才能有限。咱们这次发动的服务中有6个进程,每个进程的线程池中有1个线程,也便是服务器最多只能一起处理6个恳求。这儿咱们建议7个恳求,并发数为6,意味着同一时刻最多有6个恳求抵达咱们的服务器。刚好可以被咱们的6个进程实例处理,每个进程的线程池又只要一个线程,因而相当于每个线程池处理1个恳求,那便是有6个线程等候CPU调度,4个内核轮流履行这6个线程,很显然,每个内核平均履行1.5个线程。假如一个pbkdf2耗时550毫秒,那么平均每个内核需求履行550 * 1.5=825毫秒,也便是一个恳求需求耗时825毫秒左右。这仍是抱负状况,没考虑CPU在进程之间切换的时刻开支。这也是为啥咱们从控制台的输出得到850毫秒左右的恳求耗时。

因而,尽管咱们可以一起处理一切这些传入的恳求,但终究成果是咱们的全体功能受到影响。由于咱们的CPU需求一起调度处理这6个恳求。

这次,咱们只创建4个进程实例,数量坚持和咱们核算机CPU内核数一样。

process.env.UV_THREADPOOL_SIZE = 1;
const cluster = require('cluster')
console.log(cluster.isMaster)
if (cluster.isMaster) {
    cluster.fork();
    cluster.fork();
    cluster.fork();
    cluster.fork();
} else {
    const express = require('express')
    const crypto = require('crypto')
    const app = express();
    app.get('/', (req, res) => {
        let startTime = Date.now();
        console.log('承受恳求:', startTime)
        crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
            console.log('恳求耗时:', Date.now() - startTime)
            res.send('Hi there')
        })
    })
    app.get('/fast', (req, res) => {
        res.send('This was fast!')
    })
    app.listen(3000)
}

从下图可以看出,最快的恳求只需求555毫秒即可处理完结。而最慢的恳求需求1135毫秒处理完结。因而,尽管咱们运用了更少的进程,但实际上咱们终究的功能是变得更好的。

Node Cluster形式之进程数量是否越多越好?

因而,通过大幅添加运用程序内部的子进程数量,使其超出核算机CPU的内核数量,将对咱们的服务功能发生净负面的影响。

运用PM2敞开cluster mode

一般在生产环境中,咱们并不直接运用 nodejs 的cluster模块敞开多进程实例,而是运用pm2。pm2提供了进程看护,比方进程实例挂了主动重启等才能。

一般在开发环境不运用pm2

新建index.js文件:

const express = require('express')
const crypto = require('crypto')
const app = express();
app.get('/', (req, res) => {
    let startTime = Date.now();
    console.log('承受恳求:', startTime)
    crypto.pbkdf2('a', 'b', 100000, 512, 'sha512', () => {
        console.log('恳求耗时:', Date.now() - startTime)
        res.send('Hi there')
    })
})
app.get('/fast', (req, res) => {
    res.send('This was fast!')
})
app.listen(3000)

发动服务

pm2 start index.js -i 0

Node Cluster形式之进程数量是否越多越好?

中止服务

pm2 delete index

Node Cluster形式之进程数量是否越多越好?

罗列一切进程

pm2 list

Node Cluster形式之进程数量是否越多越好?

显示进程信息

pm2 show index

Node Cluster形式之进程数量是否越多越好?

进程监控

pm2 monit

Node Cluster形式之进程数量是否越多越好?