实时聊天应用是一种在线沟通渠道,允许您进行实时对话。越来越多的开发者正在利用 Redis 的强大功能,因为它速度极快,并且支持 Lists、Sets、Sorted Sets、Hashes 等多种丰富的数据结构。Redis 还具备 Pub/Sub 消息发布订阅功能,允许开发者通过生成多个服务器实例来扩展后端。
虽然关于 Redis 的传统聊天应用教程仍然富有启发性,但我们鼓励您深入学习我们的AI 聊天机器人教程,以释放现代对话体验的潜力。
请注意,此代码是开源的。您可以在本教程末尾找到链接。
在本教程中,我们将学习如何使用 Flask、Socket.IO 和 Redis 开发实时消息应用。此示例利用 Redis 的 Pub/Sub 功能结合 websockets 来实现客户端和服务器之间的实时聊天应用通信。
请注意,此代码是开源的,并实现了实时聊天应用的基本功能。您可以在本教程末尾找到链接。
为了开发这个即时消息应用,您需要以下软件
首先,我们将克隆实现基本聊天功能的项目。
git clone https://github.com/redis-developer/basic-redis-chat-app-demo-python
cd client
yarn install
要运行聊天应用的前端,请运行以下命令
yarn start
You can now access a chat window in the browser.
Local: http://localhost:3000
On Your Network: http://192.168.1.9:3000
cd ..
pip3 install -r requirements.txt
要启动完整的聊天应用,请运行以下命令
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 http://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 字符串来保存消息结构,并简化此演示应用的实现细节。
向“General”房间填充消息。消息被添加到 ID 为 `room:0` 的有序集合中
初始化后,会创建一个发布/订阅订阅:`SUBSCRIBE MESSAGES`。同时,每个服务器实例将在此通道上运行一个监听器,以接收实时更新。
同样,为简单起见,每条消息都序列化为 JSON,我们解析并以与 WebSocket 消息相同的方式处理。
发布/订阅允许连接用不同平台编写的多个服务器,而无需考虑每个服务器的实现细节。
实例化 WebSocket/实时服务器时,它会监听以下事件
使用键为 `online_users` 的全局集合来保存每个用户的在线状态。因此,在新连接时,用户 ID 会被写入该集合
SADD online_users 1
这里我们将 ID 为 1 的用户添加到了 `online_users` 集合中
之后,向客户端广播消息,通知他们新用户已加入聊天。
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` 通道,并在消息到达后进行分发。请注意,服务器通过独立的事件流(由 Server Sent Events 处理)传输发布/订阅消息,这是因为需要独立于 socket.io 信号运行发布/订阅消息循环。
聊天数据存储在各种键和各种数据类型中。用户数据存储在哈希集合中,每个用户条目包含以下值
username:唯一用户名;
password:哈希密码
此外,用户还关联了一组聊天室
房间是有序集合,其中包含消息,分数字段是每条消息的时间戳
每个聊天室都有一个关联的名称
“online”集合是所有用户的全局集合,用于跟踪哪些用户在线。
每个用户哈希集合通过键 user:{userId}
访问。其数据使用 HSET key field data
命令存储。用户 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 user:2:rooms
这将返回 ID 为 2 的用户的聊天室 ID
ZREVRANGE room:{roomId} {offset_start} {offset_end}
。例如: ZREVRANGE room:1:2 0 50
它将返回 ID 为 1 和 2 的用户之间的私聊房间的 50 条消息,偏移量为 0。