加入收藏 | 设为首页 | 会员中心 | 我要投稿 好传媒网 (https://www.haochuanmei.com/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 编程要点 > 语言 > 正文

node中进程通信怎样实现,你知道哪些方式?

发布时间:2022-01-11 14:25:24 所属栏目:语言 来源:互联网
导读:这篇文章给大家分享的是node中进程通信的实现,下文给大家介绍了五种实现方式,文中示例代码供大家参考,需要的朋友可以了解看看,接下来就跟随小编一起学习一下吧。 通信其实涵盖开发的各个层面,常见的有客户端和服务端通过各种通信协议进行通信,RPC通信
    这篇文章给大家分享的是node中进程通信的实现,下文给大家介绍了五种实现方式,文中示例代码供大家参考,需要的朋友可以了解看看,接下来就跟随小编一起学习一下吧。
 
 
 
    通信其实涵盖开发的各个层面,常见的有客户端和服务端通过各种通信协议进行通信,RPC通信,开发过程中各个模块之间的相互通信,electron主进程和渲染进程之间的通信等等;本文主要是尝试总结下nodejs(单线程,多线程,多进程)通信的方式,使用场景,实现等。
 
    通信的实现方式
    一般进程通信的实现方式如下:
 
    1、Shared Memory(内存共享);
 
    2、Socket(套接字);
 
    3、管道(非命名管道Pipe, 命名管道FIFO);
 
    4、Signal(信号);
 
    5、Message queue(消息队列);
 
    下面我们看下在node中如何实现这些方式的通信
 
    一、Shared Memory(内存共享)
    单机下(客户端内单线程,单进程里多线程,单台服务器内多进程),通过内存共享实现通信的方式最为常见。
 
    Shared Memory(内存共享)-单线程
    从操作系统层面来看,进程内所有线程内存都是共享的,但前提是需要知道内存的访问地址。
 
    但从语言层面(node或者说是v8的实现层面),我们没有直接接触内存的管理,而是间接从v8提供的语法/api进行内存操作。v8提供三种方式给我们共享内存(也许叫共享变量更恰当):全局变量, 局部变量, 共享传参(call by sharing);
 
    v8在执行代码之前会先将代码通过Estree规范转化为抽象语法树后再进行解释编译执行,在抽象语法树中(关于抽象语法树可查看我另外一篇文章)是有scope的,而内存读取是通过标志符(变量命名)逐级往上回溯查找。所以如果你需要在两个方法之间共享某个内存,可以在他们共同的作用域下进行创建。
 
    Shared Memory(内存共享)-多线程
    在客户端环境或node环境,我们都可以实现多线程,两者方式也类似(node通过worker_threads实现,浏览器通过Worker实现)。这里的内存共享主要是借助内存操作的api(SharedArrayBuffer)实现的。先看下浏览器实现的例子:
 
// 主线程
const buffer = new SharedArrayBuffer(1024)
const typedArr = new Int16Array(buffer)
const newWorker = new Worker('./worker.js')
 
typedArr[0] = 20
 
newWorker.postMessage(buffer)
newWorker.onmessage= (data) => {
    console.group('[the main thread]');
    console.log('Data received from the main thread: %i', typedArr[0]);
    console.groupEnd();
}
// 子线程
addEventListener('message', ({ data }) => {
  const arr = new Int16Array(data)
 
  console.group('[the worker thread]')
  console.log('Data received from the main thread: %i', arr[0])
  console.groupEnd()
  arr[0] = 18
  
  postMessage('Updated')
})
// 结果
[the worker thread]
  Data received from the main thread: 20
[the main thread]
  Data received from the main thread: 18
    Shared Memory(内存共享)-多进程
    因为进程启动后内存是无法相互读取的(系统层面的限制),进程之间的内存共享实际是通过新开辟一段shared memory实现的。但node暂时没有支持shared memory,只能通过低级语言来实现,例如: c++实现的 shared-memory-disruptor addon插件(另外文章介绍)。
 
    二、Socket(套接字)
    Socket 分两种实现:    
 
    1、TCP Socket;    
 
    2、UNIX Domain Socket;    
 
    两者的主要区别如下:
 
TCP Socket适用于单机,C/S架构等.但UNIX Domain Socket只适用于单机。  
UNIX Domain Socket不需要经过一系列的网络中转(协议,分包,校验等等),性能更高,稳定性更好。
    TCP Socket
    概念: TCP Socket是应用层与TCP/IP协议族通信的中间抽象层,是一种操作系统提供的进程间通信机制;
 
    TCP Socket通信应该是我们日常开发(C/S架构)中最常见的通信方式之一,在我们日常开发中最常见的就是各种应用层协议(http,websocket,rpc,ftp等)的使用,node中http模块也是基于net模块实现的。
 
    注:其实UDP也属于TCP分层(并不是严格的指TCP通信,而是网络通信层中的TCP/IP层),node有提供'dgram'模块来实现,但在实际应用中没有接触过,所以不进行了解。
 
    net
    在node中,TCP Socket是由net模块实现的,net模块主要提供了以下功能:
 
    1、上层的IPC支持(实际上是管道通信的实现,后面管道通信再详细说明);
 
    2、net.Server类;
 
// 服务端通过net.createServer创建服务,会返回net.Server对象,可以通过返回值进行各种事件监听,端口监听
const net = require('net')
 
net.createServer((server => {
  server.end(`hello world!n`)
})).listen(3302, () => {
  console.log(`running ...`)
})
    3、net.Socket类;
 
const net = require('net')
const socket = net.createConnection({port: 3302})
 
socket.on('data', data => {
  console.log(data.toString())
})
    UNIX Domain Socket
    UNIX Domain Socket是通过创建一个文件描述符,不同进程之间的通信通过读写这个文件描述符进行通信(可以分为创建进程和其他进程,其他进程之间的相互通信可以通过创建进程作为中转)。e.g.
 
// 创建进程
const net = require('net')
const unixSocketServer = net.createServer(server => {
  server.on('data', data => {
    console.log(`receive data: ${data}`)
  })
})
 
unixSocketServer.listen('/tmp/test', () => {
  console.log('listening...')
})
// 其他进程
const net = require('net')
const socket = net.createConnection({path: '/tmp/test'})
 
socket.on('data', data => {
  console.log(data.toString())
})
socket.write('my name is vb')
// 输出结果
listening...
receive data: my name is vb
    三、管道
    管道通信分两种,非命名管道和命名管道。    
 
    非命名管道的实现方式和UNIX Domain Socket一样,都是通过创建文件描述符进行通信。
 
    命名管道是通过固定的文件描述符进行通信:
 
"\.pipe" + PIPE_NAME;
    源码可参考stackoverflow(https://stackoverflow.com/questions/11750041/how-to-create-a-named-pipe-in-node-js)
目前理解的管道通信和UNIX Domain Socket实现基本一致,只是管道通信规范了读写权限,半双工通信,UNIX Domain Socket更加自由一些。
 
    四、Signal(信号)
    Signal是操作系统在终止进程前给进程发送的信号。在node中可以通过process.kill(pid, signal)/child_process.kill(pid, signal)接口实现,e.g.
 
// 要被终止的http守护进程
const Koa = require('koa')
const app = new Koa()
 
app.listen(3004, () => {
  console.log(`process pid is : ${process.pid}`) // process pid is : 75208
})
// 操作进程
process.kill(75208, 'SIGHUP')
// 'SIGHUP'是一般结束进程的信号,还有更多其他的信号参考 [标识](https://blog.csdn.net/houjixin/article/details/71430489)
    但这里的前提是你需要获取到被终止的进程pid,更多pid的内容可阅读我之前关于进程的文章。
 
    五、Message queue(消息队列)
    一开始我以为是redis,各种MQ之类的基于TCP的消息队列。但其实是操作系统内的消息队列,node暂时没有提供相关的上层接口,需要更底层实现,e.g. svmq

(编辑:好传媒网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    热点阅读