nodejs玩儿转进程

序言

  1. nodejs是如何充分利用多核cup 服务器的?
  2. 如何保证进程的稳健型?

正文

因为Node运行在V8引擎上,我们的JavaScript 将会运行在单个进程的单个线程上。它带来的好处是: 程序状态是单一的,在没有多线程的情况 下没有锁、线程同步问题,操作系统在调度时也因为较少上下文的切换,可以很好地提高CPU的使用率

从严格的意义上而言,Node并非真正的单线程架构,Node自身还有 一定的I/O线程存在,这些I/O线程由底层libuv处理,这部分线程对于JavaScript开发者而言是透明 的,只在C++扩展开发时才会关注到

进程和线程的区别及优劣:
1进程是操作系统分配资源的最小单元

多进程的缺点主要体现在

	1 无法共享内部状态(进程池的方式可以解决)
	2 以及创建和销毁进程时候

多线程相对多进程的优点:

创建和销毁线程相对进程来说开销小很多,(并且线程之间可以共享数据 ,内存浪费的问题得
以解决)并且利用线程池可以减少创建和销毁线程的开销

多线程的缺点:

每个线程都有自己独立的堆栈,每个堆栈都要占用一定的内存空间
服务模型的变迁:

从“古”到今,Web服务器的架构已经历了几次变迁。从服务器处理客户端请求的并发量这个纬度来看,每次变迁都是里程碑的见证
在这里插入图片描述
由此来看 多线程和事件驱动都有自己弊端

事件驱动:CPU的计算能力决定这类服务的性能上线
多线程模式:受资源上限的影响
那么nodejs是如何充分利用多核cup 服务器的?

答案是通过fork进程的方式 ,我们再一次将经典的示例代码存为worker.js文件,代码如下:

var http = require('http'); http.createServer(function (req, res) {
   

	res.writeHead(200, {
   'Content-Type': 'text/plain'});
	res.end('Hello World\n');
	
}).listen(Math.round((1 + Math.random()) * 1000), '127.0.0.1');

通过node worker.js启动它,将会侦听1000到2000之间的一个随机端口
将以下代码存为master.js,并通过node master.js启动它:

/**
 * 充分利用cup的资源同时启动在多个进程上启动服务 
*/
const cpus = require("os").cpus();
const fork = require("child_process").fork;

for (let index = 0; index < cpus.length; index++) {
   
  fork("./worker.js");
}

这段代码将会根据当前机器上的CPU数量复制出对应Node进程数。在*nix系统下可以通过ps aux | grep worker.js查看到进程的数量,如下所示
在这里插入图片描述

创建子进程

child_process 模块赋予了node可以随意创建子进程的能力 ,它提供了4个方法用于创建子进程:

spawn(): 启动一个子进程来执行命令
exec: 启动一个子进程来执行命令,与spawn不同的是其接口不同,他有一个回掉函数来获知子进程的状况。
execFile():启动一个子进程来执行可执行文件。
fork():与spawn()类似,不同点在于它创建Node的子进程只需指定要执行的JavaScript文件模块即可。

spawn()与exec()、execFile()的不同是:
后两者创建时可以指定timeout属性设置超时时间,一旦创建的进程运行超过设定的时间将会
被杀死。

exec()与execFile()不同的是,exec()适合执行已有的命令execFile()适合执行文件。这里我们以一个寻常命令为例,node worker.js分别用上述4种方法实现,如下所示

var cp = require('child_process');
//spawn
cp.spawn('node', ['worker.js']);
//exec
cp.exec('node worker.js', function (err, stdout, stderr) {
   
    // some code 
});
//execFile
cp.execFile('worker.js', function (err, stdout, stderr) {
    
	// some code
}); 
//fork
cp.fork('./worker.js');

在这里插入图片描述

如果是JavaScript文件通过execFile()运行,它的首行内容必须添加如下代码

#!/usr/bin/env node

尽管4种创建子进程的方式有些差别,但事实上后面3种方法都是spawn()的延伸应用

进程间通信

主线程与工作线程之间通过onmessage()和postMessage()进行通信,子进程对象则由send() 方法实现主进程向子进程发送数据message事件实现收听子进程发来的数据,与API在一定 程度上相似。通过消息传递内容,而不是共享或直接操作相关资源,这是较为轻量和无依赖 的做法

parent.js

// 
var cp = require('child_process');
var n = cp.fork(__dirname + '/sub.js');
n.on('message', function (m) {
    console.log('PARENT got message:', m);
});
n.send({
   hello: 'world'});

sub.js

process.on('message', function (m) {
    
	console.log('CHILD got message:', m);
});
process.send({
   foo: 'bar'});

通过fork()或者其他API,创建子进程之后,为了实现父子进程之间的通信,父进程与子进程之间将会创建IPC通道。通过IPC通道,父子进程之间才能通过message和send()传递消息

进程间通信原理

IPC的全称是Inter-Process Communication,即进程间通信
进程间通信的目的是为了让不同的进程能够互相访问资源并进行协调工作

实现进程间通信的技术有很多,如
命名管道
匿名管道
socket
信号量
共享内存
消息队列
Domain Socket

Node中实现IPC通道的是管道(pipe) 技术。但此管道非彼管道,在Node中管道是个抽象层面的称呼,具体细节实现由libuv提供,在 Windows下由命名管道(named pipe)实现,*nix系统则采用Unix Domain Socket实现。表现在应用层上的进程间通信只有简单的message事件和send()方法,接口十分简洁和消息化。下图为IPC 创建和实现的示意图。
在这里插入图片描述
父进程在实际创建子进程之前,会创建IPC通道并监听它,然后才真正创建出子进程并通 过环境变量(NODE_CHANNEL_FD)告诉子进程这个IPC通道的文件描述符。子进程在启动的过程中, 根据文件描述符去连接这个已存在的IPC通道,从而完成父子进程之间的连接
在这里插入图片描述

句柄传递

建立好进程之间的IPC后,如果仅仅只用来发送一些简单的数据,显然不够我们的实际应用 使用
如果让服务都监听 到相同的端口,将会有什么样的结果?

这时只有一个工作进程能够监听到该端口上,其余的进程在监听的过程中都抛出了 EADDRINUSE异常,这是端口被占用的情况,新的进程不能继续监听该端口了。这个问题破坏了我 们将多个进程监听同一个端口的想法。要解决这个问题,通常的做法是让每个进程监听不同的端 口,其中主进程监听主端口(如80),主进程对外接收所有的网络请求,再将这些请求分别代理 到不同的端口的进程上。示意图如图9-4所示。
在这里插入图片描述
通过代理,可以避免端口不能重复监听的问题,甚至可以在代理进程上做适当的负载均衡, 使得每个子进程可以较为均衡地执行任务。由于进程每接收到一个连接,将会用掉一个文件描述 符,因此代理方案中客户端连接到代理进程,代理进程连接到工作进程的过程需要用掉两个文件 描述符。操作系统的文件描述符是有限的,代理方案浪费掉一倍数量的文件描述符的做法影响了 系统的扩展能力

主进程代码如下所示
var child = require('child_process')
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
在Node.js中,可以使用log4js库来实现日志打印和旋。首先,你需要引入log4js库并创建一个logger对象。然后,你可以使用logger对象的方法来打印日志信息。例如,使用logger.info('hello world')可以打印出"hello world"这个日志信息。\[1\] 另外,如果你想要实现日志的旋,可以使用logrotate工具。logrotate是一个用于管理日志文件的工具,它可以按照一定的规则对日志文件进行旋,以便于管理和存储。你可以在logrotate的配置文件中指定日志文件的路径、旋的规则和频率等信息,然后通过定时任务来执行logrotate命令,实现日志的旋。这样,你就可以定期地将旧的日志文件进行备份或删除,以保持日志文件的大小和数量的控制。\[1\] 另外,如果你想要在Node.js中实现全局中间件,可以使用express框架。首先,你需要引入express库并创建一个express应用程序对象。然后,你可以使用app.use()方法将中间件函数注册为全局中间件。全局中间件将对所有的客户端请求进行处理。例如,你可以创建一个中间件函数mw,然后使用app.use(mw)将其注册为全局中间件。这样,每当有客户端请求到达时,中间件函数mw都会被调用。\[2\] 另外,如果你想要在Node.js中实现错误级中间件,可以使用express框架。错误级中间件函数接收四个参数,分别是err、req、res和next。你可以在错误级中间件函数中处理错误,并通过调用next()函数将控制权传递给下一个中间件函数。例如,你可以创建一个错误级中间件函数mw,然后使用app.use(mw)将其注册为全局中间件。这样,当发生错误时,错误级中间件函数mw会被调用。\[3\] #### 引用[.reference_title] - *1* [nodejs实践录:log4js日志的使用](https://blog.csdn.net/subfate/article/details/87909927)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [NodeJS Web 框架 Express 之中间件](https://blog.csdn.net/qq_44879989/article/details/128762782)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insert_down1,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值