云脉IM的高性能消息路由与离线推送机制摘要:消息的“零丢失、低延迟”之道

源码:shuai.68api.cn

摘要:消息的"零丢失、低延迟"之道

在企业IM环境中,消息的可靠性实时性是至高要求。云脉IM如何在高并发、多设备、复杂网络状态下,实现消息的快速、精准路由,并保证离线消息的零丢失投递?


一、高性能消息路由:从连接到投递的毫秒级旅程

消息路由是IM系统的核心功能。云脉IM采用分离式路由架构,将用户状态管理、连接管理与消息路由解耦,以实现极致性能。

1. 内存级用户状态与连接管理

为了实现毫秒级查找和路由,云脉IM的信令服务器在内存中维护了一个高效的路由表。

  • 数据结构优化: 使用高性能的 ConcurrentHashMapGuava Cache 来存储 <UserID, Connection/SessionID> 的映射关系。这种结构保证了在多线程环境下的查找、插入和删除操作的高并发性。

  • 状态同步: 当用户多端登录或状态发生变化时,信令服务器通过 Redis Pub/SubKafka 等消息中间件,将最新的用户在线状态广播到所有消息处理节点(如果系统是分布式部署),确保路由表的全局一致性。

2. 消息的快速路由决策

当信令服务器接收到一条WSS消息后,路由决策流程如下:

  1. 解析协议: 快速解析消息协议包(例如,JSON或Protobuf),提取发送者ID、目标ID(或群组ID)。

  2. 目标查找: 根据目标ID,在内存路由表中查找其对应的所有在线会话(Session)

    • 单聊: 查找到一个或多个Session(多端在线)。

    • 群聊: 查找到群组内所有在线用户的Session列表。

  3. 并发投递: 使用线程池并发地将消息推送给查找到的每一个Session。

💡 Java代码片段:基于内存表的路由实现

Java

复制代码
public class MessageRouter {

    // 内存中的活跃连接映射 (UserID -> List<WebSocketSession>)
    private final ConcurrentHashMap<String, List<WebSocketSession>> userSessionsMap = new ConcurrentHashMap<>();
    
    // 异步投递线程池,防止阻塞主信令线程
    private final ExecutorService deliveryPool = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors() * 2);

    /**
     * 将消息异步路由给目标用户(支持多端在线)
     */
    public void routeMessage(String targetUserId, String payload) {
        List<WebSocketSession> sessions = userSessionsMap.get(targetUserId);

        if (sessions != null && !sessions.isEmpty()) {
            sessions.forEach(session -> {
                deliveryPool.submit(() -> {
                    try {
                        // 1. 检查连接状态 (Session.isOpen())
                        if (session.isOpen()) {
                            // 2. 异步发送消息
                            session.sendMessage(new TextMessage(payload));
                        } else {
                            // 3. 处理死连接清理
                            // sessionManager.remove(session); 
                        }
                    } catch (IOException e) {
                        logger.error("Error sending message to user: {}", targetUserId, e);
                    }
                });
            });
        }
        // 如果 sessions 为 null 或 empty,则进入离线消息处理流程
        else {
            offlineQueueManager.enqueue(targetUserId, payload);
        }
    }
    // ... 其他方法:处理用户上线、下线、多端登录逻辑
}

二、 离线消息推送:零丢失的保障机制

高性能路由解决了在线 消息的实时性,而离线消息推送 则解决了可靠性可达性

1. "存储-队列"双保险机制

云脉IM采用消息持久化离线队列相结合的双保险机制。

  1. 持久化(MySQL): 所有消息(无论是实时还是离线)都会被首先写入 MySQL 数据库。这保证了消息的消息漫游回溯能力 ,是消息零丢失的根本保证。

  2. 离线队列(Redis/Kafka): 当路由决策判断目标用户当前不在线 时,该消息的元数据或引用ID会被写入到目标用户的离线消息队列 中(通常使用 Redis ListKafka Topic)。

2. 上线后的自动补发与ACK确认

当离线用户重新上线时,系统会触发补发流程:

  1. 用户上线: 客户端建立WSS连接,信令服务器更新路由表。

  2. 队列检查: 信令服务器立即检查该用户在离线队列中的积压消息。

  3. 消息补发: 从队列中取出消息ID,从MySQL数据库中加载完整消息体,并通过WSS通道补发给用户。

  4. 客户端ACK: 客户端接收到消息后,发送一个确认包 (ACK) 给服务器。只有收到ACK后,服务器才会从持久化存储和离线队列中将该消息标记为已读或删除。

3. 多通道融合:保障移动端可达性

对于移动端(Android/iOS)用户,当应用被系统挂起(进程被杀)时,WSS连接会被断开,此时需要使用系统级的离线通知

云脉IM的解决方案是多通道融合推送

  • iOS: 通过 APNs (Apple Push Notification service) 通道。

  • Android: 优先使用国内厂商的官方通道(华为PushKit、小米Push等),如果无法使用,则回退到 FCM (Firebase Cloud Messaging)

这种策略确保了即使应用处于后台或被杀死,用户也能通过系统通知栏接收到消息提醒,显著提升了消息的可达性。

总结:自主可控是性能的基石

云脉IM通过深度定制的Java高性能路由层精细化的离线推送逻辑,实现了对消息生命周期的完全掌控。

这套技术体系,不仅保障了消息的实时性(毫秒级路由)和可靠性(存储-队列双保险),更重要的是,它完全部署在企业自有基础设施上,结合源代码交付 ,为企业提供了无与伦比的性能优势、安全保障和技术自主权


相关推荐
Seven971 小时前
剑指offer-46、孩⼦们的游戏(圆圈中最后剩下的数)
java
serendipity_hky1 小时前
互联网大厂Java面试故事:核心技术栈与场景化业务问题实战解析
java·spring boot·redis·elasticsearch·微服务·消息队列·内容社区
我真不会起名字啊1 小时前
C、C++中的sprintf和stringstream的使用
java·c语言·c++
十点摆码1 小时前
Spring Boot2 使用 Flyway 管理数据库版本
java·flyway·数据库脚本·springboo2·数据库脚本自动管理
毕设源码-钟学长1 小时前
【开题答辩全过程】以 基于Javaweb的电动汽车充电桩管理系统为例,包含答辩的问题和答案
java·spring boot
多敲代码防脱发2 小时前
为何引入Spring-cloud以及远程调用(RestTemplate)
java·开发语言
毕设源码-邱学长2 小时前
【开题答辩全过程】以 基于JavaWeb的家庭理财管理系统的设计与实现为例,包含答辩的问题和答案
java
sailing-data2 小时前
【SE】接口标准化
java·开发语言
t***p9352 小时前
idea创建springBoot的五种方式
java