在开发实时应用或者聊天室等实时通讯产品时,常常会使用 Socket.io 库来实现双向通信。而在多台服务器间协调此类通讯,则需要使用适配器来处理不同服务器和进程间的消息同步和负载均衡等问题。本文将介绍如何在 Socket.io 中使用 Redis 适配器,实现多进程间的通讯。
Redis 适配器基本原理
下面我们来了解 Redis 适配器的基本原理。在 Socket.io 实现多进程通讯时,每个进程会维护一个 or 多个 Socket.io 中的房间(Room)。当有新的连接、断开、发送消息等事件发生时,每个进程都需要通知其他进程更新房间中的状态,这就需要使用一个共享内存来存储所有进程的信息。在 Socket.io 中内置了一个 Adapter 接口 来实现进程间的通信。而 Redis 适配器则是一种常用的实现。
Redis 适配器的原理是将各个进程中的房间和事件信息,以 WebSocket 的协议格式存储在 Redis 的数据库中。每个进程都可以从 Redis 数据库中获取并处理其他进程发送的事件。当各个进程中发送消息时,Redis 适配器负责将消息存储到 Redis 数据库,并将其转发给其他进程,从而实现多进程间的通讯。
使用 Redis 适配器
下面我们来介绍如何在 Socket.io 中使用 Redis 适配器。
安装 Redis 适配器
首先需要安装 Redis 适配器依赖,可以使用 npm 安装。在终端中执行以下命令:
$ npm install socket.io-redis
初始化 Socket.io
在初始化 Socket.io 时,需要传入一个 Redis 适配器实例。例如:
const io = require('socket.io')(3000); const redisAdapter = require('socket.io-redis'); io.adapter(redisAdapter({ host: 'localhost', port: 6379 }));
这里我们创建了一个 Socket.io 实例并将其绑定默认端口 3000。然后使用 socket.io-redis
模块创建了一个 Redis 适配器实例,并将其传给 io.adapter()
方法。这样就完成了 Redis 适配器的初始化。
验证 Redis 适配器是否工作
为了验证 Redis 适配器是否工作,我们可以在多个进程中启动 Socket.io 服务,并尝试发送消息。如果消息能够在各个服务之间传递,那么就说明 Redis 适配器已经正确工作。
下面是示例代码:
-- -------------------- ---- ------- -- ---------- ----- -- - --------------------------- ----- ------------ - --------------------------- ------------------------- ----- ------------ ----- ---- ---- ---------------- ------ -- - ----------------------- --------------- -------------- -- - ------------------- --- ------------------ -- ------ ----------------- ---- -- - ------------------------- ---- -------- ------- ----- ---------------- - ---------- --- ---
我们在两个窗口中分别执行以下代码,分别启动两个进程:
$ node example.js
然后在控制台中可以看到如下输出:
Connected: <socket-id>
这个输出表明我们已经成功连接 Socket.io。我们还可以在浏览器中打开 http://localhost:3000,然后在控制台中运行以下代码,发送一条消息到其中一个进程:
const socket = io(); socket.emit('pong', new Date().getTime());
然后在控制台中可以看到以下输出:
<socket-id> pong <timestamp>, latency <latency>ms
这个输出表明我们的 Socket.io 进程已经可以接收和发送消息,Redis 适配器已经可以工作正常了。
Socket.io 房间同步
现在我们已经成功启动了两个进程,并可以在两个进程之间发送消息。接下来我们来看一下在多个进程中如何使用 Socket.io 的房间(Room)。
注意事项
在使用 Socket.io 的 Rooms 功能时,需要注意一个问题:因为使用 Redis 适配器的原因,任何对 room 的修改操作,如添加、删除客户端,都需要在 redis 中处理并广播。所以要注意如果做了集群的连接,命名空间需要保持一致。
添加房间
在 Socket.io 中,可以使用 join()
方法将客户端添加到指定的房间中。例如:
socket.join('roomA');
这个方法将该连接添加到名为 roomA
的房间中。如果房间不存在,则会自动创建该房间。
离开房间
可以使用 leave()
方法将客户端从一个房间中删除。例如:
socket.leave('roomA');
这个方法会将该连接从 roomA
房间中删除。如果该连接没有加入房间,则此操作被忽略。
向房间广播消息
可以使用 to()
方法向指定房间中的所有客户端广播消息。例如:
io.to('roomA').emit('message', 'Hello, RoomA!');
这个方法将一个 message
事件广播到名为 roomA
的房间中的所有 Socket 客户端。
分离命名空间
如果你需要使用多个 Socket.io 分离的命名空间,可以使用 of()
方法来创建每个命名空间的实例。例如:
const chatNamespace = io.of('/chat'); chatNamespace.on('connect', socket => { console.log(`New chat connection: ${socket.id}`); socket.on('message', message => { chatNamespace.emit('message', message); // 广播消息给所有连接 }); });
在上面的代码中,我们创建了一个名为 /chat
的命名空间,并为其添加一个 connect
事件监听器。当有新的连接时,我们打印出连接的 ID 并添加一个 message
事件监听器。当有消息到达时,我们使用 chatNamespace.emit()
方法将消息广播给该命名空间下的所有连接。
总结
本文介绍了在 Socket.io 中使用 Redis 适配器来实现多进程间通信的方法。使用 Redis 适配器,可以让 Socket.io 库更加稳定和可靠。同时,也可以更好地将 Socket.io 这类实时通讯库应用在生产环境中。
来源:JavaScript中文网 ,转载请注明来源 https://www.javascriptcn.com/post/6490713348841e9894e97593