最有效的node.js进程间通信库/方法是什么?

发布于 2024-11-17 02:40:30 字数 111 浏览 7 评论 0 原文

我们有几个应该能够传递消息的 Node.js 进程, 最有效的方法是什么? 如何使用node_redis pub/sub

编辑:进程可能在不同的机器上运行

We have few node.js processes that should be able to pass messages,
What's the most efficient way doing that?
How about using node_redis pub/sub

EDIT: the processes might run on different machines

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

野却迷人 2024-11-24 02:40:31

为什么不使用 ZeroMQ/0mq 进行 IPC? Redis(一种数据库)对于做 IPC 这样简单的事情来说有点过分了。

引用指南:

ØMQ(ZeroMQ、0MQ、zmq)看起来像一个嵌入式网络库
但其行为就像一个并发框架。它为您提供了带有
跨各种传输的原子消息,例如进程内、
进程间、TCP 和多播。您可以将插座 N 对 N 连接
扇出、发布-订阅、任务分配和请求-回复等模式。
它的速度足够快,可以成为集群产品的结构。它是
异步 I/O 模型为您提供可扩展的多核应用程序,
构建为异步消息处理任务。

使用 0MQ(或者甚至通过 Node 核心中的网络库使用普通套接字,减去 0MQ 套接字提供的所有功能)的优点是没有主进程。其无代理设置最适合您描述的场景。如果您只是从一个中央进程将消息推送到各个节点,您可以在 0mq 中使用 PUB/SUB 套接字(还支持通过 PGM/EPGM 的 IP 多播)。除此之外,0mq 还提供各种不同的套接字类型(PUSH/PULL/XREP/XREQ/ROUTER/DEALER),您可以使用它们创建自定义设备。

从这个优秀的指南开始:
http://zguide.zeromq.org/page:all

对于 0MQ 2.x:

http://github.com/JustinTulloss/zeromq.node

对于 0MQ 3.x(上述模块的分支。这支持 PUBLISHER 侧过滤PUBSUB):

http://github.com/shripadk/zeromq.node

Why not use ZeroMQ/0mq for IPC? Redis (a database) is over-kill for doing something as simple as IPC.

Quoting the guide:

ØMQ (ZeroMQ, 0MQ, zmq) looks like an embeddable networking library
but acts like a concurrency framework. It gives you sockets that carry
atomic messages across various transports like in-process,
inter-process, TCP, and multicast. You can connect sockets N-to-N with
patterns like fanout, pub-sub, task distribution, and request-reply.
It's fast enough to be the fabric for clustered products. Its
asynchronous I/O model gives you scalable multicore applications,
built as asynchronous message-processing tasks.

The advantage of using 0MQ (or even vanilla sockets via net library in Node core, minus all the features provided by a 0MQ socket) is that there is no master process. Its broker-less setup is best fit for the scenario you describe. If you are just pushing out messages to various nodes from one central process you can use PUB/SUB socket in 0mq (also supports IP multicast via PGM/EPGM). Apart from that, 0mq also provides for various different socket types (PUSH/PULL/XREP/XREQ/ROUTER/DEALER) with which you can create custom devices.

Start with this excellent guide:
http://zguide.zeromq.org/page:all

For 0MQ 2.x:

http://github.com/JustinTulloss/zeromq.node

For 0MQ 3.x (A fork of the above module. This supports PUBLISHER side filtering for PUBSUB):

http://github.com/shripadk/zeromq.node

五里雾 2024-11-24 02:40:31

我将从节点提供的内置功能开始。
您可以使用进程信号,例如:

process.on('SIGINT', function () {
  console.log('Got SIGINT.  Press Control-D to exit.');
});

此信号

当进程收到信号时发出。请参阅 sigaction(2) 了解
标准 POSIX 信号名称列表,例如 SIGINT、SIGUSR1 等。

一旦您了解了进程,您就可以创建一个 child-处理并将其连接到message事件以检索和发送消息。使用 child_process.fork() 时,您可以使用 child.send(message, [sendHandle]) 向子进程写入消息,并且消息由 'message' 事件接收孩子。

另外 - 您可以使用集群。集群模块允许您轻松创建所有共享服务器端口的进程网络。

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', function(worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  });
} else {
  // Workers can share any TCP connection
  // In this case its a HTTP server
  http.createServer(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");
  }).listen(8000);
}

对于第三方服务,您可以检查:
hook.io信号bean

i would start with the built in functionality that node provide.
you can use process signalling like:

process.on('SIGINT', function () {
  console.log('Got SIGINT.  Press Control-D to exit.');
});

this signalling

Emitted when the processes receives a signal. See sigaction(2) for a
list of standard POSIX signal names such as SIGINT, SIGUSR1, etc.

Once you know about process you can spwn a child-process and hook it up to the message event to retrive and send messages. When using child_process.fork() you can write to the child using child.send(message, [sendHandle]) and messages are received by a 'message' event on the child.

Also - you can use cluster. The cluster module allows you to easily create a network of processes that all share server ports.

var cluster = require('cluster');
var http = require('http');
var numCPUs = require('os').cpus().length;

if (cluster.isMaster) {
  // Fork workers.
  for (var i = 0; i < numCPUs; i++) {
    cluster.fork();
  }

  cluster.on('exit', function(worker, code, signal) {
    console.log('worker ' + worker.process.pid + ' died');
  });
} else {
  // Workers can share any TCP connection
  // In this case its a HTTP server
  http.createServer(function(req, res) {
    res.writeHead(200);
    res.end("hello world\n");
  }).listen(8000);
}

For 3rd party services you can check:
hook.io, signals and bean.

π浅易 2024-11-24 02:40:31

看一下node-messenger

https://github.com/weixiyen/messenger.js

将通过自动维护的连接池轻松满足大多数需求(发布/订阅...即发即忘..发送/请求)

take a look at node-messenger

https://github.com/weixiyen/messenger.js

will fit most needs easily (pub/sub ... fire and forget .. send/request) with automatic maintained connectionpool

手长情犹 2024-11-24 02:40:31

我们正在开发多进程节点应用程序,需要处理大量实时跨进程消息。

我们先尝试了redis-pub-sub,未能满足要求。

然后尝试了tcp socket,这更好,但仍然不是最好的。

所以我们改用 UDP 数据报,速度要快得多。

这是代码仓库,只有几行代码。
https://github.com/SGF-Games/node-udpcomm

we are working on multi-process node app, which is required to handle large number of real-time cross-process message.

We tried redis-pub-sub first, which failed to meet the requirements.

Then tried tcp socket, which was better, but still not the best.

So we switched to UDP datagram, that is much faster.

Here is the code repo, just a few of lines of code.
https://github.com/SGF-Games/node-udpcomm

最笨的告白 2024-11-24 02:40:31

几年前,我需要使用另一种语言(Perl;)在 Web 服务器进程之间进行 IPC。在通过共享内存、Unix 信号(例如 SIGINT 和信号处理程序)以及其他选项研究 IPC 后,我最终选择了一些非常简单、运行良好且速度快的方法。但是,如果您的进程并非全部都有权访问同一文件系统,那么它可能不符合要求。

这个概念是使用文件系统作为通信通道。在我的世界中,我有一个 EVENTS 目录,其下的子目录将消息定向到适当的进程:例如 /EVENTS/1234/player1 和 /EVENTS/1234/player2,其中 1234 是具有两个不同玩家的特定游戏。如果一个进程想要了解特定玩家在游戏中发生的所有事件,它可以使用(在 Node.js 中)监听 /EVENTS/1234/player1:

fs.watch
(或 fsPromises.watch)

如果进程想要监听特定游戏的所有事件,只需为 fs.watch 设置“recursive: true”选项即可观看 /EVENTS/1234。或者观看 /EVENTS 以查看所有消息 - fs.watch 生成的事件将告诉您修改了哪个文件路径。

举一个更具体的例子,我的世界我有player1的网络浏览器客户端监听 服务器发送事件 (SSE),并且在一个特定 Web 服务器进程中运行一个循环来发送这些事件。现在,为player2 提供服务的Web 服务器进程想要向为player1 运行SSE 的服务器进程发送消息(IPC),但不知道可能是哪个进程;它只是在 /EVENTS/1234/player1 中写入(或修改)一个文件。该目录正在通过 fs.watch 在处理 player1 的 SSE 的 Web 服务器进程中进行监视。我发现这个系统非常灵活、快速,而且它还可以设计为留下所有发送的消息的记录。我使用它是为了让多个随机 Web 服务器进程中的一个可以与另一个特定 Web 服务器进程进行通信,但它也可以以 N 对 1 或 1 对 N 的方式使用。

希望这对某人有帮助。您基本上是让操作系统和文件系统为您完成工作。以下是有关 MacOS 和 Linux 中如何工作的几个链接:

https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/FSEvents_ProgGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40005289

https://man7.org/linux/man-pages/man7/inotify.7.html

您在任何语言中使用的任何模块都会挂接到这样的 API 中。我已经 30 多年没有花太多时间摆弄 Windows 了,所以我不知道文件系统事件是如何工作的,但我敢打赌有一个等效的。

编辑(来自不同平台的更多信息https://nodejs.org/dist/latest-v19.x/docs/api/fs.html#fswatchfilename-options-listener):

警告#
fs.watch API 跨平台并非 100% 一致,并且在某些情况下不可用。

在 Windows 上,如果监视的目录被移动或重命名,则不会发出任何事件。删除监视目录时报EPERM错误。

可用性#
此功能取决于底层操作系统是否提供了一种通知文件系统更改的方式。

在 Linux 系统上,这使用 inotify(7)。
在 BSD 系统上,这使用 kqueue(2)。
在 macOS 上,这对文件使用 kqueue(2),对目录使用 FSEvents。
在 SunOS 系统(包括 Solaris 和 SmartOS)上,这使用事件端口。
在 Windows 系统上,此功能取决于 ReadDirectoryChangesW。
在 AIX 系统上,此功能依赖于 AHAFS,必须启用它。
在 IBM i 系统上,不支持此功能。
如果底层功能由于某种原因不可用,那么 fs.watch() 将无法运行并可能引发异常。例如,当使用 Vagrant 或 Docker 等虚拟化软件时,在网络文件系统(NFS、SMB 等)或主机文件系统上监视文件或目录可能不可靠,在某些情况下是不可能的。

仍然可以使用 fs.watchFile(),它使用统计轮询,但这种方法速度较慢且不太可靠。

编辑2: https://www.npmjs.com/package/node-watch 是在某些平台上可能有帮助的包装器

I needed IPC between web server processes in another language (Perl;) a couple years ago. After investigating IPC via shared memory, and via Unix signals (e.g. SIGINT and signal handlers), and other options, I finally settled on something quite simple which works quite well and is fast. It may not fit the bill if your processes do not all have access to the same file system, however.

The concept is to use the file system as the communication channel. In my world, I have an EVENTS dir, and under it sub dirs to direct the message to the appropriate process: e.g. /EVENTS/1234/player1 and /EVENTS/1234/player2 where 1234 is a particular game with two different players. If a process wants to be aware of all events happening in the game for a particular player, it can listen to /EVENTS/1234/player1 using (in Node.js):

fs.watch
(or fsPromises.watch)

If a process wanted to listen to all events for a particular game, simply watch /EVENTS/1234 with the 'recursive: true' option set for fs.watch. Or watch /EVENTS to see all msgs -- the event produced by fs.watch will tell you the which file path was modified.

For a more concrete example, I my world I have the web browser client of player1 listening for Server-Sent Events (SSE), and there is a loop running in one particular web server process to send those events. Now, a web server process servicing player2 wants to send a message (IPC) to the server process running the SSEs for player1, but doesn't know which process that might be; it simply writes (or modifies) a file in /EVENTS/1234/player1. That directory is being watched -- via fs.watch -- in the web server process handling SSEs for player1. I find this system very flexible, and fast, and it can also be designed to leave a record of all messages sent. I use it so that one random web server process of many can communicate to one other particular web server process, but it could also be used in an N-to-1 or 1-to-N manner.

Hope this helps someone. You're basically letting the OS and the file system do the work for you. Here are a couple links on how this works in MacOS and Linux:

https://developer.apple.com/library/archive/documentation/Darwin/Conceptual/FSEvents_ProgGuide/Introduction/Introduction.html#//apple_ref/doc/uid/TP40005289

https://man7.org/linux/man-pages/man7/inotify.7.html

Any module you're using in whatever language is hooking into an API like one of these. It's been 30+ years since I've fiddled much with Windows, so I don't know how file system events work there, but I bet there's an equivalent.

EDIT (more info on different platforms from https://nodejs.org/dist/latest-v19.x/docs/api/fs.html#fswatchfilename-options-listener):

Caveats#
The fs.watch API is not 100% consistent across platforms, and is unavailable in some situations.

On Windows, no events will be emitted if the watched directory is moved or renamed. An EPERM error is reported when the watched directory is deleted.

Availability#
This feature depends on the underlying operating system providing a way to be notified of file system changes.

On Linux systems, this uses inotify(7).
On BSD systems, this uses kqueue(2).
On macOS, this uses kqueue(2) for files and FSEvents for directories.
On SunOS systems (including Solaris and SmartOS), this uses event ports.
On Windows systems, this feature depends on ReadDirectoryChangesW.
On AIX systems, this feature depends on AHAFS, which must be enabled.
On IBM i systems, this feature is not supported.
If the underlying functionality is not available for some reason, then fs.watch() will not be able to function and may throw an exception. For example, watching files or directories can be unreliable, and in some cases impossible, on network file systems (NFS, SMB, etc) or host file systems when using virtualization software such as Vagrant or Docker.

It is still possible to use fs.watchFile(), which uses stat polling, but this method is slower and less reliable.

EDIT2: https://www.npmjs.com/package/node-watch is a wrapper that may help on some platforms

回忆那么伤 2024-11-24 02:40:31

并不是每个人都知道 pm2 有一个 API 通过它您可以与其进程进行通信。

// pm2-call.js:
import pm2 from "pm2";

pm2.connect(() => {
    pm2.sendDataToProcessId(
        {
            type: "process:msg",
            data: {
                some: "data",
                hello: true,
            },
            id: 0,
            topic: "some topic",
        },
        (err, res) => {}
    );
});

pm2.launchBus((err, bus) => {
    bus.on("process:msg", (packet) => {
        packet.data.success.should.eql(true);
        packet.process.pm_id.should.eql(proc1.pm2_env.pm_id);
        done();
    });
});
// pm2-app.js:
process.on("message", (packet) => {
    process.send({
        type: "process:msg",
        data: {
            success: true,
        },
    });
});

Not everybody knows that pm2 has an API thanks to which you can communicate to its processes.

// pm2-call.js:
import pm2 from "pm2";

pm2.connect(() => {
    pm2.sendDataToProcessId(
        {
            type: "process:msg",
            data: {
                some: "data",
                hello: true,
            },
            id: 0,
            topic: "some topic",
        },
        (err, res) => {}
    );
});

pm2.launchBus((err, bus) => {
    bus.on("process:msg", (packet) => {
        packet.data.success.should.eql(true);
        packet.process.pm_id.should.eql(proc1.pm2_env.pm_id);
        done();
    });
});
// pm2-app.js:
process.on("message", (packet) => {
    process.send({
        type: "process:msg",
        data: {
            success: true,
        },
    });
});

笨笨の傻瓜 2024-11-24 02:40:30

如果您想将消息从一台机器发送到另一台机器并且不关心回调,那么 Redis pub/sub 是最好的解决方案。它非常容易实现,而且 Redis 速度非常快。

首先,您必须在其中一台机器上安装 Redis。

连接到 Redis 真的很容易:

var client = require('redis').createClient(redis_port, redis_host);

但不要忘记在防火墙中打开 Redis 端口!

然后你必须为每台机器订阅某个频道:

client.on('ready', function() {
  return client.subscribe('your_namespace:machine_name');
});

client.on('message', function(channel, json_message) {
  var message;
  message = JSON.parse(json_message);
  // do whatever you vant with the message
});

你可以跳过 your_namespace 并使用全局命名空间,但你迟早会后悔的。

发送消息也非常容易:

var send_message = function(machine_name, message) {
  return client.publish("your_namespace:" + machine_name, JSON.stringify(message));
};

如果您想发送不同类型的消息,您可以使用 pmessages 而不是 messages:

client.on('ready', function() {
  return client.psubscribe('your_namespace:machine_name:*');
});

client.on('pmessage', function(pattern, channel, json_message) {
  // pattern === 'your_namespace:machine_name:*'
  // channel === 'your_namespace:machine_name:'+message_type
  var message = JSON.parse(message);
  var message_type = channel.split(':')[2];
  // do whatever you want with the message and message_type
});

send_message = function(machine_name, message_type, message) {
  return client.publish([
    'your_namespace',
    machine_name,
    message_type
  ].join(':'), JSON.stringify(message));
};

最佳实践是根据进程(或机器)的功能来命名(例如 'send_email' )。在这种情况下,如果进程(或机器)实现了不止一种功能,则可以订阅多个频道。

实际上,使用redis 建立双向通信是可能的。但它更棘手,因为它需要向每条消息添加唯一的回调通道名称,以便在不丢失上下文的情况下接收回调。

所以,我的结论是:如果您需要“发送后忘记”通信,请使用 Redis;如果您需要成熟的双向通信,请研究其他解决方案

If you want to send messages from one machine to another and do not care about callbacks then Redis pub/sub is the best solution. It's really easy to implement and Redis is really fast.

First you have to install Redis on one of your machines.

Its really easy to connect to Redis:

var client = require('redis').createClient(redis_port, redis_host);

But do not forget about opening Redis port in your firewall!

Then you have to subscribe each machine to some channel:

client.on('ready', function() {
  return client.subscribe('your_namespace:machine_name');
});

client.on('message', function(channel, json_message) {
  var message;
  message = JSON.parse(json_message);
  // do whatever you vant with the message
});

You may skip your_namespace and use global namespace, but you will regret it sooner or later.

It's really easy to send messages, too:

var send_message = function(machine_name, message) {
  return client.publish("your_namespace:" + machine_name, JSON.stringify(message));
};

If you want to send different kinds of messages, you can use pmessages instead of messages:

client.on('ready', function() {
  return client.psubscribe('your_namespace:machine_name:*');
});

client.on('pmessage', function(pattern, channel, json_message) {
  // pattern === 'your_namespace:machine_name:*'
  // channel === 'your_namespace:machine_name:'+message_type
  var message = JSON.parse(message);
  var message_type = channel.split(':')[2];
  // do whatever you want with the message and message_type
});

send_message = function(machine_name, message_type, message) {
  return client.publish([
    'your_namespace',
    machine_name,
    message_type
  ].join(':'), JSON.stringify(message));
};

The best practice is to name your processes (or machines) by their functionality (e.g. 'send_email'). In that case process (or machine) may be subscribed to more than one channel if it implements more than one functionality.

Actually, it's possible to build a bi-directional communication using redis. But it's more tricky since it would require to add unique callback channel name to each message in order to receive callback without losing context.

So, my conclusion is this: Use Redis if you need "send and forget" communication, investigate another solutions if you need full-fledged bi-directional communication.

尛丟丟 2024-11-24 02:40:30

在提出问题 4 年多后,出现了一个名为 node-ipc 的进程间通信模块。它支持unix/windows socket在同一台机器上通信,也支持TCP、TLS和UDP,声称至少socket、TCP和UDP是稳定的。

以下是取自 github 存储库文档的一个小示例:

Server for Unix Sockets, Windows Sockets & Server for Unix Sockets, Windows Sockets & Server for Unix Sockets, Windows Sockets & TCP 套接字

var ipc=require('node-ipc');

ipc.config.id   = 'world';
ipc.config.retry= 1500;

ipc.serve(
    function(){
        ipc.server.on(
            'message',
            function(data,socket){
                ipc.log('got a message : '.debug, data);
                ipc.server.emit(
                    socket,
                    'message',
                    data+' world!'
                );
            }
        );
    }
);

ipc.server.start();

Unix 套接字和客户端TCP 套接字

var ipc=require('node-ipc');

ipc.config.id   = 'hello';
ipc.config.retry= 1500;

ipc.connectTo(
    'world',
    function(){
        ipc.of.world.on(
            'connect',
            function(){
                ipc.log('## connected to world ##'.rainbow, ipc.config.delay);
                ipc.of.world.emit(
                    'message',
                    'hello'
                )
            }
        );
        ipc.of.world.on(
            'disconnect',
            function(){
                ipc.log('disconnected from world'.notice);
            }
        );
        ipc.of.world.on(
            'message',
            function(data){
                ipc.log('got a message from world : '.debug, data);
            }
        );
    }
);

我目前正在评估此模块是否可以替代本地 ipc(但将来可能是远程 ipc),作为通过 stdin/stdout 的旧解决方案的替代品。也许当我完成后我会扩展我的答案,以提供更多信息该模块如何工作以及有多好。

More than 4 years after the question being ask there is an interprocess communication module called node-ipc. It supports unix/windows sockets for communication on the same machine as well as TCP, TLS and UDP, claiming that at least sockets, TCP and UDP are stable.

Here is a small example taken from the documentation from the github repository:

Server for Unix Sockets, Windows Sockets & TCP Sockets

var ipc=require('node-ipc');

ipc.config.id   = 'world';
ipc.config.retry= 1500;

ipc.serve(
    function(){
        ipc.server.on(
            'message',
            function(data,socket){
                ipc.log('got a message : '.debug, data);
                ipc.server.emit(
                    socket,
                    'message',
                    data+' world!'
                );
            }
        );
    }
);

ipc.server.start();

Client for Unix Sockets & TCP Sockets

var ipc=require('node-ipc');

ipc.config.id   = 'hello';
ipc.config.retry= 1500;

ipc.connectTo(
    'world',
    function(){
        ipc.of.world.on(
            'connect',
            function(){
                ipc.log('## connected to world ##'.rainbow, ipc.config.delay);
                ipc.of.world.emit(
                    'message',
                    'hello'
                )
            }
        );
        ipc.of.world.on(
            'disconnect',
            function(){
                ipc.log('disconnected from world'.notice);
            }
        );
        ipc.of.world.on(
            'message',
            function(data){
                ipc.log('got a message from world : '.debug, data);
            }
        );
    }
);

Im currently evaluating this module for a replacement local ipc (but could be remote ipc in the future) as a replacement for an old solution via stdin/stdout. Maybe I will expand my answer when I'm done to give some more information how and how good this module works.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文