ホームページ  >  記事  >  ウェブフロントエンド  >  ノードにおけるプロセス通信のいくつかの実装方法の詳細な説明

ノードにおけるプロセス通信のいくつかの実装方法の詳細な説明

青灯夜游
青灯夜游転載
2021-12-14 19:04:514868ブラウズ

nodeプロセス間で通信するにはどうすればよいですか?次の記事は、ノード プロセスの通信方法と、これらの通信方法をノードに実装する方法を理解するのに役立ちます。

ノードにおけるプロセス通信のいくつかの実装方法の詳細な説明

通信は、実際には開発のすべてのレベルをカバーします。一般的なものには、さまざまな通信プロトコルを介したクライアントとサーバーの通信、RPC 通信、開発プロセス中のさまざまなモジュール間の通信が含まれます。 、メインの電子プロセスとレンダリング プロセス間の通信など;

この記事では主に nodejs (シングルスレッド、マルチスレッド、マルチプロセス) の通信方法を要約します。 、シナリオ、実装などを使用します。

#通信の実装方法

一般的なプロセス通信は次のように実装されます:

1. 共有メモリ (メモリ共有);

2. ソケット (ソケット);

3. パイプ (名前なしパイプ Pipe、名前付きパイプ FIFO);

4. シグナル (信号);

5 、メッセージ キュー (メッセージ キュー);

これらの通信方法をノードに実装する方法を見てみましょう

1. 共有メモリ (メモリ共有)

スタンドアロン(クライアント内の単一スレッド、単一プロセス内の複数のスレッド、単一サーバー内の複数のプロセス)、メモリ共有による通信が最も一般的な方法です。

共有メモリ (メモリ共有) - シングル スレッド

オペレーティング システム レベルから見ると、プロセス内のすべてのスレッド メモリは共有されますが、前提として、メモリが必要であるということです。既知のアクセスアドレスになります。

しかし、言語レベル (ノードまたは v8 実装レベル) から見ると、メモリ管理には直接触れず、v8 が提供する構文/API から間接的にメモリ操作を実行します。 v8 では、メモリを共有する 3 つの方法 (共有変数と呼ぶほうが適切かもしれません) を提供します:

グローバル変数ローカル変数共有パラメータ(共有による呼び出し)。

コードを実行する前に、v8 はまず Estree 仕様を通じてコードを抽象構文ツリーに変換し、それから解釈、コンパイル、実行します。抽象構文ツリー内 (抽象構文ツリーについては、私の他のを参照してください)記事), スコープがあり、メモリの読み取りは識別子(変数の名前付け)を段階的に遡って行われます。したがって、2 つのメソッド間でメモリを共有する必要がある場合は、共通のスコープ内にメモリを作成できます。

共有メモリ (メモリ共有) - マルチスレッド

クライアント環境またはノード環境では、マルチスレッドを実装できます。これら 2 つの方法は似ています (ノードは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 (メモリ共有)-multi-process

プロセスが完了するとメモリが相互に読み取れなくなるため、 (システム レベルの制限) が開始されると、プロセス間のメモリ共有は、実際には共有メモリの新しいセクションを開くことによって実現されます。ただし、ノードは当面共有メモリをサポートしておらず、低レベル言語 (たとえば、c で実装されたshared-memory-disruptor アドオン プラグイン) を介してのみ実装できます (別の記事で説明)。

2. ソケット (ソケット)

ソケットは 2 つの実装に分かれています:

1. TCP ソケット;
2. UNIX ドメイン ソケット;
これらの主な違いは、違いは次のとおりです。

TCP Socket适用于单机,C/S架构等.但UNIX Domain Socket只适用于单机。  
UNIX Domain Socket不需要经过一系列的网络中转(协议,分包,校验等等),性能更高,稳定性更好。

TCP ソケット

概念: TCP ソケットは、アプリケーション層と TCP/IP プロトコル ファミリの間の通信のための中間抽象化層です。 、オペレーティング システムによって提供されます プロセス間通信メカニズム;

TCP ソケット通信は、日常の開発 (C/S アーキテクチャ) で最も一般的な通信方法の 1 つです。はさまざまなアプリケーション層プロトコル (http、websocket、rpc、ftp など) であり、ノード内の http モジュールも net モジュールに基づいて実装されます。

注: 実際、UDP は TCP 層にも属します (厳密には TCP 通信を指すのではなく、ネットワーク通信層の TCP/IP 層を指します)。ノードはそれを実装するための「dgram」モジュールを提供しますが、実際、私はアプリケーションでこれにさらされたことがないので、それについてはあまり知りません。

net

ノードでは、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 はファイルディスクリプタを作成し、このファイルディスクリプタの読み書きにより異なるプロセス間の通信を行います(作成プロセスと作成プロセスに分けられます)。他のプロセスはリレーとしてプロセスを作成することで相互に通信できます)。例:

// 创建进程
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

3. パイプライン

パイプライン通信は、非名前付きパイプと名前付きパイプの 2 つのタイプに分類されます。

名前のないパイプの実装は、ファイル記述子を作成して通信する UNIX ドメイン ソケットと同じです。
名前付きパイプは固定ファイル記述子を通じて通信します:

"\\\\.\\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

更多node相关知识,请访问:nodejs 教程!!

以上がノードにおけるプロセス通信のいくつかの実装方法の詳細な説明の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjuejin.cnで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。