Node.js的进程管理的深入理解(3)

利用fork实现master-worker模型

首先来看看,如果我们在child.js中启动一个http服务会发生什么情况。

// master.js
const { fork } = require('child_process')

for (let i = 0; i < 2; i++) {
 const child = fork('./child.js')
}

// child.js
const http = require('http')
http.createServer((req, res) => {
 res.end('Hello World\n');
}).listen(8000)

       +--------------+
       |       |
       |  master  |
       |       |
   +--------+--------------+- -- -- -
   |                 |
   |             Error: listen EADDRINUSE
   |                 |
   |
+----v----+           +-----v---+
|     |           |     |
| worker1 |           | worker2 |
|     |           |     |
+---------+           +---------+
  :8000              :8000

我们fork了两个子进程,因为两个子进程同时对一个端口进行监听,Node会直接抛出一个异常(Error: listen EADDRINUSE),如上图所示。那么我们能不能使用代理模式,同时监听多个端口,让master进程监听80端口收到请求时,再将请求分发给不同服务,而且master进程还能做适当的负载均衡。

       +--------------+
       |       |
       |  master  |
       |   :80   |
   +--------+--------------+---------+
   |                 |
   |                 |
   |                 |
   |                 |
+----v----+           +-----v---+
|     |           |     |
| worker1 |           | worker2 |
|     |           |     |
+---------+           +---------+
  :8000              :8001

但是这么做又会带来另一个问题,代理模式中十分消耗文件描述符(linux系统默认的最大文件描述符限制是1024),文件描述符在windows系统中称为句柄(handle),习惯性的我们也可以称linux中的文件描述符为句柄。当用户进行访问,首先连接到master进程,会消耗一个句柄,然后master进程再代理到worker进程又会消耗掉一个句柄,所以这种做法十分浪费系统资源。为了解决这个问题,Node的进程间通信可以发送句柄,节省系统资源。

句柄是一种特殊的智能指针 。当一个应用程序要引用其他系统(如数据库、操作系统)所管理的内存块或对象时,就要使用句柄。

我们可以在master进程启动一个tcp服务,然后通过IPC将服务的句柄发送给子进程,子进程再对服务的连接事件进行监听,具体代码如下:

// master.js
var { fork } = require('child_process')
var server = require('net').createServer()
server.on('connection', function(socket) {
 socket.end('handled by master') // 响应来自master
})
server.listen(3000, function() {
 console.log('master listening on: ', 3000)
})
for (var i = 0; i < 2; i++) {
 var child = fork('./child.js')
 child.send('server', server) // 发送句柄给worker
 console.log('worker create, pid is ', child.pid)
}

// child.js
process.on('message', function (msg, handler) {
 if (msg !== 'server') {
  return
 }
 // 获取到句柄后,进行请求的监听
 handler.on('connection', function(socket) {
  socket.end('handled by worker, pid is ' + process.pid) 
 })
})
      

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:http://www.heiqu.com/242.html