Skip to content
bitzorcas
中EN

Concept

Chat 即时通讯模块架构概览:WebSocket、Redis 广播与消息持久化

深入解析 BitzOrcas.Modern 即时通讯体系,掌握 ChatSession 会话聚合、WebSocket/SignalR 长连接、Redis Pub/Sub 跨节点广播与离线消息同步。

Last updated

在高并发协作与企业即时通讯(Chat Subsystem)中,系统面临百万级长连接与实时广播的双重压力:

  • 跨服务器节点消息广播(Multi-Node Backplane):用户 A 连接在集群节点 1,用户 B 连接在节点 2,节点 1 必须将消息毫秒级路由给节点 2;
  • 弱网离线与乱序同步(Offline Message Gap):移动端在电梯等弱网断线重连后,必须通过 Sequence 序号精准拉取未读消息差量,杜绝消息丢失或重复展示;
  • 租户频道安全鉴权:严禁未获授权的用户通过伪造频道 ID 监听跨租户或私密频道的群聊消息。

BitzOrcas.Modern Chat 模块以“WebSocket 接入层 + Redis Backplane 广播底座 + 顺序递增消息存储”构建了高可用实时通讯引擎。

即时通讯长连接与跨节点广播拓扑

1. 客户端 A (发送消息)

2. Web 集群节点 1 (WebSocket 端点)

3. ChatMessage 消息持久化 (递增 SequenceNo 落库)

4. Redis Pub/Sub Backplane (频道: chat:tenant:channel_01)

5. Web 集群节点 2 (监听该频道)

6. 客户端 B (WebSocket 实时推送下发)


第一步:顺序消息实体 ChatMessage

消息实体采用严格递增的序列号(SequenceNo),供客户端断线增量同步:

ChatMessage.cs: 聊天消息实体
using BitzOrcas.Domain.Entities;
using BitzOrcas.Persistence.Metadata;
namespace BitzOrcas.Chat.Domain;
// 1. 声明消息流水物理表与频道唯一索引
[BitzTable("SysChatMessage", IsTenant = true, IsSoftDelete = false, Description = "聊天消息流水表")]
[BitzIndex("IX_SysChatMessage_Channel_Seq", nameof(ChannelId), nameof(SequenceNo), IsUnique = true)]
public sealed class ChatMessage : Entity<string>
{
[BitzColumn(Length = 64, IsRequired = true)]
public string ChannelId { get; set; } = string.Empty;
[BitzColumn(Length = 64, IsRequired = true)]
public string SenderUserId { get; set; } = string.Empty;
// 2. 单频道内单调递增序号(1, 2, 3... 用于客户端增量拉取与离线同步)
[BitzColumn(IsRequired = true)]
public long SequenceNo { get; set; }
[BitzColumn(Length = 4000, IsRequired = true)]
public string Content { get; set; } = string.Empty;
[BitzColumn(IsRequired = true)]
public DateTimeOffset SentAt { get; set; }
}

第二步:发送消息垂直切片与跨节点广播

SendChatMessageCommandHandler.cs: 消息发送切片
using BitzOrcas.Application.Abstractions.Realtime;
using BitzOrcas.Domain.Results;
using BitzOrcas.Chat.Domain;
public sealed class SendChatMessageCommandHandler(
IChatMessageRepository messageRepo,
IRealtimePublisher realtimePublisher,
IAppClock clock)
{
public async ValueTask<Result> Handle(SendChatMessageCommand command, CancellationToken ct)
{
// 1. 获取当前频道下一个序列号并保存消息
var nextSeq = await messageRepo.GetNextSequenceNoAsync(command.ChannelId, ct);
var msg = new ChatMessage
{
ChannelId = command.ChannelId,
SenderUserId = command.SenderUserId,
SequenceNo = nextSeq,
Content = command.Content,
SentAt = clock.UtcNow
};
await messageRepo.SaveAsync(msg, ct);
// 2. 通过 Redis Backplane 向集群广播给频道订阅者
await realtimePublisher.PublishToChannelAsync(
channel: $"chat:{command.ChannelId}",
payload: new ChatMessageBroadcastDto(msg.Id, msg.SenderUserId, msg.Content, msg.SequenceNo),
ct);
return Result.Success();
}
}

总结

Chat 模块提供低延迟与可靠性:

  • 单调 Sequence 序号:离线重连通过 SyncAfter(lastSeq) 精准补齐消息差量;
  • Redis 分布式广播:集群线性横向扩展,支撑万级并发在线;
  • 多租户物理隔离:频道名称天然携带租户前缀,杜绝越权串号。

100%

滚轮或按钮缩放 · 放大后拖动画面 · 双击切换 100% / 200%