V2EX = way to explore
V2EX 是一个关于分享和探索的地方
Sign Up Now
For Existing Member  Sign In
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
goofish
50.01D
0.29D
V2EX  ›  程序员

IM 中如何处理节点故障,导致消息不可靠问题?请教

  •  
  •   goofish · Feb 17, 2023 · 2957 views
    This topic created in 1176 days ago, the information mentioned may be changed or developed.

    最近在做一个 IM 的开发,初次尝试 IM 开发,碰到点问题向懂这块的兄弟们请教一下。直接上图: IM 技术方案 先简单说一下技术方案吧:

    • 用户登录之后与一台 server 建立连接,会话信息保存到本地( SessionMap ),就是把 userId 和 channel 的映射关系存起来,再把 userId 和 server 所在主机 MAC 地址的映射关系存到 redis 中。

    • 每台 server 在启动的时候,会在 MQ 里订阅自己所在主机的 MAC 主题(这里是把主机 MAC 作为 MQ 的 Topic 用的)。

    • 两个 user 之间相互发消息:

      • 如果两个 user 在同一台主机,直接从本地 SessionMap 获取对方 channel 完成消息传递;

      • 如果两个 user 在不同的主机,一个 user 发完消息后,server 端从 redis 中获取对方所在主机 MAC (假设为 MAC_2 ),然后把消息丢到 MQ 的 MAC_2 Topic 中,传递到对方主机,再从对方主机 SessionMap 中取出对方 channel ,完成消息传递;

      • 如果是传给 MAC_3 上的 user ,user 不在线,就离线存储。

    现在问题是,假如说某台机器故障了,比如假设 MAC_3 故障,硬件损坏,那 MQ 中 MAC_3 Topic 中的消息会越积越多,而这个 Topic 又没有订阅者,那这部分信息该怎么处理?

    20 replies    2023-02-19 16:16:54 +08:00
    Nazz
        1
    Nazz  
       Feb 17, 2023 via Android
    做好容灾就行了
    billlee
        2
    billlee  
       Feb 17, 2023 via Android
    应该用 userId 的 hash 来分区。哪有人用服务器的地址分区的。
    wyx119911
        3
    wyx119911  
       Feb 17, 2023
    这架构很难维护,服务应该做成无状态的
    KristenGe
        4
    KristenGe  
       Feb 17, 2023
    @Nazz 问题是一旦量上来了,扩缩容的时候,可能会出现多台主机对应的 MQ Topic 处于无订阅状态。
    KristenGe
        5
    KristenGe  
       Feb 17, 2023
    @billlee 能具体点吗?目前 MQ 中的消息等于是通过 userId 寻址,找 Topic 送达对应主机,userId 分区是怎么个做法?
    KristenGe
        6
    KristenGe  
       Feb 17, 2023
    @wyx119911 服务如果无状态,消息寻址,或者说消息路由吧,怎么送达对方的 channel 呢?
    Nazz
        7
    Nazz  
       Feb 17, 2023 via Android
    @gemingsy im server 应该是有状态副本集, 把用户分配到固定的节点上.
    KristenGe
        8
    KristenGe  
       Feb 17, 2023
    @Nazz 这个其实没有问题的,无非就是 client 和 server 之间做一层 routing ,现在问题是不同节点上的用户之间消息交互,是通过对方机器标识(或服务标识)做 MQ 的 Topic ,精准送达,如果服务 down 了,topic 成无订阅状态,消息无法消费了。现在能想到的是用 ZK ,不过前期不想太复杂。
    javapythongo
        9
    javapythongo  
       Feb 17, 2023
    可以把用户是否在线存储到 redis 中,发送消息前,判断下,不在线就直接离线存储
    b1ghawk
        10
    b1ghawk  
       Feb 17, 2023 via Android
    可以把消息泛播到每个服务器上,
    每个服务器判定这条消息的用户是否在本机上又连接,有的话就发出去。
    huanw
        11
    huanw  
       Feb 17, 2023
    楼上的老哥说得对,消息应该散播到集群每个节点上,因为你每个节点都有 SessionMap ,由节点来决定怎么给客户端发消息。做到无状态才有利于扩展,比如断线重连,本来连接节点 1 ,断线后连接节点 2 ,这都不影响消息发送。
    mawerss1
        12
    mawerss1  
       Feb 18, 2023 via Android
    不说这个系统设计如何,单论这个问题,在你这个设计里挂了消息就只能扔掉,如果丢消息,你需要在发送端做好存储
    KristenGe
        13
    KristenGe  
       Feb 18, 2023 via Android
    @b1ghawk 这种做法我感觉在 IM 的系统中不太现实,量一旦上来了,会造成网络堵塞;而且每台机器都等于要处理全网的消息量,性能一定会成为瓶颈
    KristenGe
        14
    KristenGe  
       Feb 18, 2023 via Android
    @mawerss1 发送端其实已经收到消息发送成功的应答了,因为消息已经落到 MQ 里了
    Nazz
        15
    Nazz  
       Feb 18, 2023 via Android
    @gemingsy 服务挂了让副本起来工作啊,每个编号的 Deployment 搞多个 Pod ,订阅同一个主题
    Nazz
        16
    Nazz  
       Feb 18, 2023 via Android
    @Nazz 小集群内做成无状态服务
    PythonYXY
        17
    PythonYXY  
       Feb 18, 2023
    路由信息单独保存在 redis 或者 kv 数据库中,抽象出一个路由服务集群统一消费 MQ 消息,对消息进行路由推送。不过要考虑路由服务的消费能力,避免过高的消息延迟。
    NUT
        18
    NUT  
       Feb 18, 2023
    通用问题。 其实必须有一个主去做 session 状态的分配。 陌陌老早时候有一个 ppt 分享他们的策略。 分三层,一层是接入层, 无状态,可以任意扩展,主要是接受链接, 第二层 逻辑层,保证消息业务逻辑的路由等, 第三层存储。

    其实你的服务没有脑子,需要有一个脑子,去做这种订阅关系的维护, 也就是 接入层上线以后, 由 master 去做分配 session 相关策略。主要保证 master 强一致就行。

    不过咋说,难度就会上一个层级。 还可以看看 codis 还有 tidb 等分布式设计。 会有帮助的。
    guonaihong
        19
    guonaihong  
       Feb 18, 2023
    收藏下下。。。
    KristenGe
        20
    KristenGe  
       Feb 19, 2023
    @NUT 如果有接入层,单论接入层的话,像连接的 channel 这种东西是作为集群各个节点本地 session 存储,还是用序列化方式统一存储,channel 这种东西适合做序列化统一存储吗?
    About   ·   Help   ·   Advertise   ·   Blog   ·   API   ·   FAQ   ·   Solana   ·   5411 Online   Highest 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 90ms · UTC 09:07 · PVG 17:07 · LAX 02:07 · JFK 05:07
    ♥ Do have faith in what you're doing.