学习

如何使用 Redis 构建聊天应用程序

Ajeet Raina
作者
Ajeet Raina, Redis 前开发人员增长经理

实时聊天应用程序是一种在线通信渠道,允许您进行实时对话。越来越多的开发人员利用 Redis 的强大功能,因为它速度极快,并且由于它支持各种丰富的数据结构,例如列表、集合、有序集合、哈希等。Redis 带有发布/订阅消息功能,允许开发人员通过生成多个服务器实例来扩展后端。

警告

虽然 Redis 上传统的聊天应用程序教程仍然很有见地,但我们鼓励您深入了解我们的 AI 聊天机器人教程,以释放现代对话体验的潜力。

信息

请注意,此代码是开源的。您可以在本教程末尾找到链接。

在本教程中,我们将了解如何使用 Flask、Socket.IO 和 Redis 开发实时消息应用程序。此示例使用 Redis 发布/订阅功能与 WebSockets 结合使用,以实现客户端和服务器之间实时聊天应用程序通信。

信息

请注意,此代码是开源的,并实现了实时聊天应用程序的基本功能。您可以在本教程末尾找到链接。

步骤 1. 先决条件#

为了执行此即时消息应用程序开发,您需要以下软件

  • Python 3.6+

步骤 2. 克隆存储库#

首先,我们将克隆实现基本聊天功能的项目。

git clone https://github.com/redis-developer/basic-redis-chat-app-demo-python

步骤 3. 安装所需的软件包#

cd client
yarn install

步骤 4. 启动前端#

要运行聊天应用程序的前端,请运行以下命令

yarn start
You can now access a chat window in the browser.

  Local:            https://localhost:3000
  On Your Network:  https://192.168.1.9:3000

步骤 5. 安装所需的 Python 模块#

cd ..
pip3 install -r requirements.txt

步骤 6. 运行后端#

要启动完整的聊天应用程序,请运行以下命令

python3 -m venv venv/
source venv/bin/activate
python3 app.py
python3 app.py
 * Restarting with stat
 * Debugger is active!
 * Debugger PIN: 220-696-610
(8122) wsgi starting up on https://127.0.0.1:5000

它是如何工作的?#

此即时消息应用程序服务器充当基本的 REST API,它涉及在聊天室中维护会话和处理用户状态(除了 WebSocket/实时部分)。当服务器启动时,会发生初始化步骤。首先,建立一个新的 Redis 连接,并检查是否需要加载演示数据。

初始化#

为简单起见,检查一个名为 total_users 的键的值:如果该键不存在,我们将在 Redis 数据库中填充初始数据。EXISTS total_users(检查键是否存在)演示数据初始化分多个步骤进行。

创建演示用户#

我们创建一个新的用户 ID:INCR total_users。然后我们根据用户名设置用户 ID 查找键:例如

SET username:nick user:1

最后,将剩余数据写入哈希集

示例

  HSET user:1 username "nick" password "bcrypt_hashed_password".

此外,每个用户都会被添加到默认的“通用”房间中。为了处理每个用户的聊天室,我们有一个集合来保存聊天室 ID。以下是如何添加房间的示例命令

 SADD user:1:rooms "0"

填充用户之间的私人消息。首先,创建私人房间:如果需要建立私人房间,则会为每个用户生成一个房间 ID:room:1:2,其中数字对应于用户 ID,按升序排列。

例如,在 2 个用户之间创建一个私人房间

 SADD user:1:rooms 1:2 and SADD user:2:rooms 1:2

然后,我们通过写入排序集合来向此房间添加消息

 ZADD room:1:2 1615480369 "{'from': 1, 'date': 1615480369, 'message': 'Hello', 'roomId': '1:2'}"

我们使用字符串化的 JSON 来保持消息结构,并简化此演示应用程序的实现细节。

用消息填充“通用”房间。消息被添加到排序集合中,该排序集合的 ID 为“通用”房间:room:0

发布/订阅#

初始化后,将创建发布/订阅订阅:SUBSCRIBE MESSAGES。同时,每个服务器实例都会在该频道上的消息上运行一个监听器,以接收实时更新。

同样,为简单起见,每条消息都被序列化为 JSON,然后我们将解析它们,并以与 WebSocket 消息相同的方式进行处理。

发布/订阅允许连接多个用不同平台编写的服务器,而无需考虑每个服务器的实现细节。

实时聊天和会话处理#

当实例化一个 WebSocket/实时服务器时,它会监听接下来的事件

  • 连接。一个新用户连接。此时,用户 ID 将被捕获并保存到会话中(该会话缓存在 Redis 中)。注意,会话缓存是语言/库特定的,这里纯粹用于持久化和在服务器重新加载之间维护状态。

一个名为 online_users 的全局集合用于保存每个用户的在线状态。因此,在新的连接上,用户 ID 会被写入该集合

 SADD online_users 1

这里我们将 ID 为 1 的用户添加到 online_users 集合中

之后,会向客户端广播一条消息,通知他们有一个新用户加入了聊天。

  • 断开连接。它与连接事件类似,除了我们需要从 online_users 集合中移除用户并通知客户端:SREM online_users 1(使 ID 为 1 的用户脱机)。
  • 消息。用户发送消息,需要向其他客户端广播。发布/订阅还允许我们将此消息广播到连接到此 Redis 的所有服务器实例:
 PUBLISH message "{'serverId': 4132, 'type':'message', 'data': {'from': 1, 'date': 1615480369, 'message': 'Hello', 'roomId': '1:2'}}"

请注意,我们发送了与消息类型和服务器 ID 相关的附加数据。服务器 ID 用于丢弃发送它们的服务器实例的消息,因为它连接到相同的 MESSAGES 频道。

序列化 JSON 的 type 字段对应于我们用于实时通信的实时方法(连接/断开连接/消息)。

data 字段是方法特定的信息。在上面的示例中,它与新消息相关。

数据是如何存储的?#

Redis 主要用作数据库,用于保存用户/消息数据以及在连接的服务器之间发送消息。

实时功能由 Socket.IO 处理,用于服务器-客户端消息传递。此外,每个服务器实例都会订阅发布/订阅的 MESSAGES 频道,并在消息到达时调度消息。请注意,服务器使用单独的事件流(由服务器发送的事件处理)传输发布/订阅消息,这是由于需要在 socket.io 信号之外运行发布/订阅消息循环。

聊天数据存储在不同的键和不同的数据类型中。用户数据存储在哈希集中,每个用户条目包含以下值

  • 用户名:唯一的用户名;

  • 密码:哈希密码

  • 此外,一组聊天室与用户相关联

  • 房间是排序集合,其中包含消息,每个消息的得分是该消息的时间戳

  • 每个聊天室都有一个与之关联的名称

  • “在线”集合对所有用户都是全局的,用于跟踪哪些用户在线。

  • 每个用户哈希集通过键 user:{userId} 访问。其数据使用 HSET key field data 存储。用户 ID 通过递增 total_users 键计算 (INCR total_users)

  • 用户名存储为单独的键 (username:{username}),返回 userId 以便快速访问,并使用 SET username:{username} {userId} 存储。

  • 用户所属的房间存储在 user:{userId}:rooms 中,作为聊天室 ID 集合。通过 SADD user:{userId}:rooms {roomId} 命令添加房间。

  • 消息存储在 room:{roomId} 键中,使用有序集合(如上所述)。它们使用 ZADD room:{roomId} {timestamp} {message} 命令添加。消息序列化为应用程序特定的 JSON 字符串。

如何访问数据?#

获取用户 HGETALL user:{id}。

 HGETALL user:2

获取 ID 为 2 的用户数据。

  • 在线用户:SMEMBERS online_users。这将返回在线用户的 ID
  • 获取用户的房间 ID:SMEMBERS user:{id}:rooms。例如:
 SMEMBERS user:2:rooms

这将返回 ID 为 2 的用户的聊天室 ID

  • 获取消息列表 ZREVRANGE room:{roomId} {offset_start} {offset_end}。例如:
 ZREVRANGE room:1:2 0 50

这将返回 ID 为 1 和 2 的用户之间的私人房间的 0 偏移量的 50 条消息。