学习

如何在 Amazon Web Services 上使用 Python 和 Redis 构建实时聊天应用程序

实时聊天消息应用程序的普及率呈指数级增长。WhatsApp、Facebook、Telegram、Slack、Discord 等移动应用程序已成为我们生活的一部分。用户沉迷于这些实时聊天移动应用程序对话,因为它们带来了个人化的触感并提供了实时互动。

越来越多的社交媒体应用程序将社交元素引入其中,以实现协作、消息传递、社交互动和评论等活动。此类活动需要实时功能才能自动向用户展示更新的信息。越来越多的开发人员正在利用 Redis 的强大功能,因为它速度极快,并且支持各种丰富的数据结构,例如列表、集合、排序集、哈希等。Redis 同时附带 Pub/Sub 消息功能,使开发人员能够通过生成多个服务器实例来扩展后端。

1. 您将构建什么?#

在本教程中,我们将了解如何构建一个使用 Flask、Socket.IO 和 Redis Cloud 在 Amazon Web Services 上运行的实时聊天应用程序。此示例使用发布/订阅功能结合 WebSockets 来实现客户端和服务器之间的消息通信。

2. 您需要什么?#

  • 前端 - React, Socket.IO
  • 后端 - Python(Flask), 托管在 AWS 上的 Redis Enterprise Cloud

3. 入门#

步骤 1:注册免费的 Redis Cloud 帐户#

按照本教程 注册免费的 Redis Cloud 帐户。如果您已有现有帐户,则只需要您的登录凭据来访问您的订阅。

在创建新订阅时选择 AWS 作为云供应商。在创建新数据库时,请确保设置自己的密码。在数据库创建过程结束时,您将获得 Redis Cloud 数据库端点和端口。保存这些信息,您以后会需要它们。

提示

 您无需创建 AWS 帐户即可设置 Redis 数据库。AWS 上的 Redis Cloud 是一款完全托管的数据库即服务,数千个客户信赖它提供高性能、无限可扩展性、真正的高可用性和一流的支持。

步骤 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 view client 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
输出
 * 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".

此外,每个用户都将添加到默认的“General”房间。为了处理每个用户的房间,我们有一个集合,其中包含房间 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 来保留消息结构并简化此演示应用程序的实现细节。您可以选择使用哈希或 JSON

用消息填充“General”房间#

消息将添加到“General”房间的排序集中,该房间的 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 键字段数据中。用户 ID 通过递增 total_users 键(INCR total_users)计算得出
  • 用户名存储为一个单独的键(username:{username}),它返回用户 ID 以便更快地访问,并使用 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 的用户之间私聊的 50 条消息,偏移量为 0。

其他资源#