1. node的单缓存
民主化是两个具有很大分立功能的流程在两个统计数据K568的一次静态继续执行的过程,是作业控制系统进行天然分权和运维的两个分立基层单位,是应用流程运转的媒介。
缓存是流程继续执行中两个单个的顺序控制流,它存有于民主化之中,是比民主化更小的能分立运转的基本上基层单位。
早期在R5 CPU 的控制系统中,为的是实现虚拟化的运转,导入了民主化的基本上概念,不同的流程运转在统计数据与命令互相隔绝的民主化中,通过天数片Tiruvanamalai运维继续执行,虽然 CPU 天数片转换与继续执行很快,因此看起来像在同一个天数运转了数个流程。
虽然民主化转换时需要留存相关硬体当晚、民主化控制块等信息,因此控制系统开支较大。为的是不断提高控制系统林宏吉率,在同一个民主化继续执行时更充分的借助 CPU 资源,导入了缓存的基本上概念。缓存是作业控制系统运维继续执行的最轻基层单位,它们依附民主化中,共享资源同一个民主化中的天然资源,基本上不保有或者只保有小量控制系统天然资源,转换开支很小。
Node是如前所述V8发动机其内构筑的,决定了他与应用程序的机制很类似。
两个node民主化根本无法借助两个核,而且node根本无法运转在单缓存中,严苛意义上,node绝非真正的单缓存构架,即两个民主化内能有数个缓存,即使node自己还有很大的i/o缓存存有,那些I/O缓存由下层的libuv处理,但那些缓存对node开发人员而言是完成透明化的,只有在C++扩充TNUMBERV12V4会加进,这里他们就过滤下层的技术细节,专门探讨他们所要关注的。
node的构架层次单缓存的益处是:流程状态单个,在没多缓存的情况下,没锁、缓存并行问题,作业控制系统在运维时,也即使极少的语句的转换,能较好地提高CPU的使用量。然而R5单缓存也有相应的优点:
那个缓存读出来后整个流程就会读出来;无法充分借助多核天然资源两个CPU运转到死,其他的CPU在看着2. node多民主化的建立
node提供更多child_process组件,那个组件中,提供更多了数个方法来建立子民主化。
const { spawn, exec, execFile, fork } = require(child_process);
这4个方法都能建立子民主化,不过使用方法还是稍微有点区别。他们以建立两个子民主化计算斐波那契数列数列为例,子民主化的文件(worker.js):
// worker.jsconst fib = (num) => {
if (num === 1 || num === 2) {
return num;
}
let a = 1, b = 2, sum = 0;
for (let i = 3; i <= num; i++) {
sum = a + b;
a = b;
b = sum;
}
return sum;
}
const num = Math.floor(Math.random() * 10) + 3;
const result = fib(num);
console.log(num, result, process.pid); // process.pid表示当前的民主化id
在master.js中如何调用那些方法建立子民主化呢?
命令使用方法解析spawnspawn(node, [worker.js])启动两个字民主化来继续执行命令execexec(node worker.js, (err, stdout, stderr) => {})启动两个子民主化来继续执行命令,有回调execFileexexFile(worker.js)启动两个子民主化来继续执行可继续执行的文件
(头部要添加#!/usr/bin/env node)forkfork(worker.js)与spawn类似,不过这里只需要自定js文件组件即可
以fork命令为例:
const { fork } = require(child_process);
const cpus = require(os).cpus();
for(let i=0, len=cpus.length; i<len; i++) {
fork(./worker.js);
}
3. 多民主化之间的通信
node中民主化的通信主要在主从(子)民主化之间进行通信,子民主化之间无法直接通信,若要互相通信,则要通过主进程进行信息的转发。
主民主化和子民主化之间是通过IPC(Inter Process Communication,民主化间通信)进行通信的,IPC也是由下层的libuv根据不同的作业控制系统来实现的。
他们还是以计算斐波那契数列数列为例,在这里,他们用cpu个数减1个的民主化来进行计算,剩余的那两个用来输出结果。这就需要负责计算的子民主化,要把结果传给主民主化,再让主民主化传给输出进行,来进行输出。这里他们需要3个文件:
master.js:用来建立子民主化和子民主化间的通信;fib.js:计算斐波那契数列;log.js:输出斐波那契数列计算的结果;主民主化:
// master.js
const { fork } = require(child_process);
const cpus = require(os).cpus();
const logWorker = fork(./log.js);
for(let i=0, len=cpus.length–1; i<len; i++) {
const worker = fork(./fib.js);
worker.send(Math.floor(Math.random()*10 + 4)); // 要计算的num
worker.on(message, (data) => { // 计算后返回的结果
logWorker.send(data); // 将结果发送给输出民主化
})
}
计算民主化:
// fib.js
const fib = (num) => {
if (num===1 || num===2) {
return num;
}
let a=1, b=2, sum=0;
for(let i=3; i<num; i++) {
sum = a + b;
a = b;
b = sum;
}
return sum;
}
process.on(message, num => {
const result = fib(num);
process.send(JSON.stringify({
num,
result,
pid: process.pid
}))
})
输出民主化:
process.on(message, data => {
console.log(process.pid, data);
})
当他们运转master时,就能看到各个子民主化计算的结果:
多进程的斐波那契数列输出结果第1个数字表示当前输出子民主化的编号,后面表示在各个子民主化计算的统计数据。
同理,他们在进行http服务日志记录时,也能采用类似的思路,数个子民主化承担http服务,剩下的子民主化来进行日志记录等操作。
当我想用子民主化建立服务器时,采用上面类似斐波那契数列的思路,将fib.js改为httpServer.js:
// httpServer.js
const http = require(http);
http.createServer((req, res) => {
res.writeHead(200, {
Content-Type: text/html
});
res.end(Math.random()+);
}).listen(8080);
console.log(http server has started at 8080, pid: +process.pid);
结果却出现错误了,提示8080端口已经被占用了:
Error: listen EADDRINUSE: address already in use :::8080
这是即使:在TCP端socket套接字监听端口有两个文件描述符,每个民主化的文件描述符都不相同,监听相同端口时就会失败。
解决方案有两种:首先最简单的就是每个子民主化都使用不同的端口,主民主化将循环的标识给子民主化,子民主化通过那个标识来使用相关的端口(例如从8080+传入的标识作为当前民主化的端口号)。
第二种方案是,在主民主化进行端口的监听,然后将监听的套接字传给子民主化。
将套接字socket发送给子民主化主民主化:
// master.js
const fork = require(child_process).fork;
const net = require(net);
const server = net.createServer();
const child1 = fork(./httpServer1.js); // randomconst child2 = fork(./httpServer2.js); // now
server.listen(8080, () => {
child1.send(server, server);
child2.send(server, server);
server.close();
})
httpServer1.js:
const http = require(http);
const server = http.createServer((req, res) => {
res.writeHead(200, {
Content-Type: text/plain
});
res.end(Math.random()+, at pid: + process.pid);
});
process.on(message, (type, tcp) => {
if (type===server) {
tcp.on(connection, socket => {
server.emit(connection, socket)
})
}
})
httpServer2.js:
const http = require(http);
const server = http.createServer((req, res) => {
res.writeHead(200, {
Content-Type: text/plain
});
res.end(Date.now()+, at pid: + process.pid);
});
process.on(message, (type, tcp) => {
if (type===server) {
tcp.on(connection, socket => {
server.emit(connection, socket)
})
}
})
他们的2个server,两个是输出随机数,两个是输出当前的天数戳,能发现这两个server都能正常的运转。同时,即使那些民主化服务是抢占式的,哪个民主化抢到连接,就哪个民主化处理请求。
他们也应当知道的是:
每个民主化之间的内存统计数据是不互通的,若他们在某一民主化中使用变量缓存了统计数据,另两个民主化是读取不到的。4. 多民主化的守护者
刚才他们在第3部分建立的多民主化,解决了多核CPU借助率的问题,接下来要解决民主化稳定的问题。
每个子民主化退出时,都会触发exit事件,因此他们通过监听exit事件来获知有民主化退出了,这时,他们就能建立两个新的民主化来替代。
const fork = require(child_process).fork;
const cpus = require(os).cpus();
const net = require(net);
const server = net.createServer();
const createServer = () => {
const worker = fork(./httpServer.js);
worker.on(exit, () => {
// 当有民主化退出时,则建立两个新的民主化
console.log(worker exit: + worker.pid);
createServer();
});
worker.send(server, server);
console.log(create worker: + worker.pid);
}
server.listen(8080, () => {
for(let i=0, len=cpus.length; i<len; i++) {
createServer();
}
})
cluster组件
在多民主化守护者这块,node也推出了cluster组件,用来解决多核CPU的借助率问题。同时cluster中也提供更多了exit事件来监听子民主化的退出。
两个经典的案例:
const cluster = require(cluster);
const http = require(http);
const cpus = require(os).cpus();
if (cluster.isMaster) {
console.log(`主民主化 ${process.pid} 正在运转`);
// 衍生工作民主化。
for (let i = 0, len=cpus.length; i < len; i++) {
cluster.fork();
}
cluster.on(exit, (worker) => {
console.log(`工作民主化 ${worker.process.pid} 已退出`);
cluster.fork();
});
} else {
http.createServer((req, res) => {
res.writeHead(200);
res.end(Math.random()+ , at pid: + process.pid);
}).listen(8080);
console.log(`工作民主化 ${process.pid} 已启动`);
}
5. 总结
node虽然是单缓存运转的,但他们能通过建立数个子民主化,来充分借助多核CPU天然资源,通过能监听民主化的一些事件,来感知每个民主化的运转状态,来提高他们项目整体的稳定性。
我是蚊子,更多的内容还能关注我的公众号:蚊子的博客。
蚊子的博客