IM专题:服务化架构IM系统(3)— 子母号

IM 系统中的 “子母号” 是 C2B 的一类典型应用,即C端用户欲与B端商家进行通信时,B端的母号会为其分配一个子号与C端用户进行聊天。

单纯设计子母号实现方案并不复杂,关键是需要兼容普通的C2C聊天方案,尽量做到方案统一和功能复用。

回顾一下,C2C聊天方案中的消息存储方式(见IM专题:分层架构IM系统(10)—Das领域模型设计):为了实现消息的定制化需求,采用 “写扩散” 模式对消息进行存储,即用户每发一条消息,站在消息发送方与消息接收方的维度分别存储一条记录,如下图。

IM专题:服务化架构IM系统(3)— 子母号

用户 uid=101 发送消息 “你好!” 给用户 uid=102,在消息表中存储如上两条记录,通过 direction 字段表征哪个 uid 是发送方或接收方。

这种 “写扩散” 的存储方式,除了可以满足不同用户对同一条消息的不同定制化需求外,更重要的是当数据规模很大需要分表时,以 to_uid 作为分表字段后,同一用户的所有聊天记录会全部落在同一张表中, 避免了对所有数据表遍历的低效操作。

子母号消息方案需要与上述方案做到最大化同一,以保证功能逻辑的独立和复用。

IM专题:服务化架构IM系统(3)— 子母号

分析一下 IM 系统中子母号应用的需求模型,如下图。

IM专题:服务化架构IM系统(3)— 子母号

子母号应用中有三类用户:C端用户、B端母号、B端子号,假设 C 端用户 uid=101,B 端母号 uid=1000,B端子号分别是 uid=1001,uid=1002,uid=1003。

用户 uid=101与母号 uid=1000通信时,母号根据策略分配一子号 uid=1001 与用户 uid=101 进行聊天。站在用户 uid=101 的角度看,与他聊天的是母号,展示的是母号的头像,但实际是子号 uid=1001 与用户 uid=101 完成的聊天过程;从业务分析来说,这是合理的,B端商家的老板以统一的形象展示在所有 C 端用户面前,当然真正干活的肯定是B端商家老板的员工子号。那么站在子号 uid=1001的角度看,与他聊天的就是用户 uid=101,母号 uid=1000只是对外的。

简单总结一下子母号需求模型:用户 uid=101 的联系人是母号 uid=1000,子号 uid=1001 的联系人是用户 uid=101;母号 uid=1000 是一个虚拟用户,不参与实际聊天,但是 B 端的商家老板,可以通过母号 uid=1000 能查询到其所有的子号与用户的聊天记录(毕竟老板有时会检查工作嘛)。

至此,问题描述就很清晰了:既要满足子母号的需求模型,又要与 C2C 的实现方案保持兼容和统一,子母号实现方案应该如何进行设计呢?

我们先把关键要点列一下:

  1. 每一条消息对不同的用户来说具有定制化需求,即若用户删除了这条消息,子号用户仍然能看到,不受影响;
  2. 数据量很大时,需要分库分表,但同一用户的所有聊天记录需要落在一张表中;
  3. C 端用户的联系人是 B 端母号;
  4. B 端子号的联系人是 C 端用户;
  5. B 端母号可以查询其所有子号与 C 端用户的聊天内容。

我们在企业应用中,实际的落地方案见下图。

IM专题:服务化架构IM系统(3)— 子母号

数据表设计体现的是子母号的领域模型设计,但比较抽象,我们举例说明:

IM专题:服务化架构IM系统(3)— 子母号

C 端用户 uid=101  发送消息 “发货吧!” 给 B 端,由子号 uid=1002 接收;在这个具体的场景中,消息存储见下图。

IM专题:服务化架构IM系统(3)— 子母号

首先,每发送一条消息,分别站在消息发送方和接收方维度在数据表中存储两条记录;通过 “写扩散” 的方式,满足消息的不同定制化需求。

uidA 字段存储消息的发送方或接收方, uidB 字段存储的则是 uidA 的联系人,那么 uidC 字段作为辅助字段则要存储第三个 uid;所以第一条记录为 uidA=101,uidB=1000, uidC=1002,第二条记录为 uidA=1002,uidB=101,uidC=1000。

在这里,uidA、uidB、uidC 存储的字段内容都会有两个身份,如何确定呢?所以引入字段 uid_sm,用来表征 uidC 存储的是母号还是子号;uidC 存储信息确定后,uidA 和 uidB 存储信息也就确定了。

一条消息,存储两条记录,如何表征消息的发送方和接收方呢?和 C2C 存储方案一致,引入字段 direction 来标志。

如此设计,满足了 “C 端用户的联系人是 B 端母号,B 端子号的联系人是 C 端用户” 的需求。

当数据量很大,进行分库分表时,以 uidA 作为分表索引字段,同一用户的所有聊天记录会落在一张表中。

B 端商家老板,想要查看一下其子号 uid=1002 与用户 uid=101的所有聊天记录,怎么做?查询条件为 uidA=1002 && uidB=101&&uid_sm=1 即可。

B 端商家老板,想要查看一下用户 uid=101 与其所有子号的聊天记录(在不同时段由不同的子号服务于同一用户是经常发生的),怎么做?查询条件为 uidA=101&&uidB=1000&&uid_sm=0 即可。

初次接触该子母号方案设计,会感觉有些许绕,但这个方案在企业中是经过实践的,确实很奇妙!

最后提一个思考题吧:当 B 端商家有非常高的用户流量时,现有的子号忙不过来,B 端老板作为母号,也想参与到服务 C 端用户的聊天中;上述子母号方案,应该如何调整来适配这个应用场景呢?

作者:棕生
来源:架构之魂
原文:https://mp.weixin.qq.com/s/bAx3ygeFk0_aIBzp4pBSgA

版权声明:本文内容转自互联网,本文观点仅代表作者本人。本站仅提供信息存储空间服务,所有权归原作者所有。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至1393616908@qq.com 举报,一经查实,本站将立刻删除。

(0)

相关推荐

发表回复

登录后才能评论