
IM电竞,IM电竞官网,IM电竞注册,电竞投注平台,电竞博彩,英雄联盟竞猜本文要分享的是B站IM消息系统的新架构升级实践总结,内容包括原架构的问题分析,新架构的整体设计以及具体的升级实现等。
按业务全域现状,在服务端角度分成客服系统、系统通知、互动通知和私信4个业务线,每个业务线内按现状标识了服务分层。私信内分为用户单聊、bToC的批量私信、群聊和应援团小助手四类,这四类细分私信没有技术解耦,单聊和批量私信比较接近系统天花板。
私信单聊发送到触达的pv转化和uv转化不足10%,有明显通过业务优化提升触达率的潜力。
1)会话列表:按聊天人排序的列表。即B站首页右上角信封一跳后看到的历史聊天人列表,以及点击未关注人等折叠会话看到的同属一类的聊天人列表。传达对方账号、最新私信和未读数的信息。点击一个会话后看到的是对聊历史,也称会线)会话详情
:pull模式。群聊每条私信只往群收件箱写一次,让成百上千的群成员在自己的设备都看到,是典型的读扩散。
:push模式。单聊每条私信既更新接收人会话也更新发送人会话,是轻微的写扩散,无系统压力。群聊有另一个不一样的特点,就是当群成员发送消息后,需要通过长链接通知其他群成员的在线设备,以及发送人其他的在线设备,这是一个写扩散的技术模型,但是这个写扩散是通知后即时销毁的,并且具有过期时间,所以仅临时占用资源,并不对存储造成压力,且能有较好的并发量。
当会话缓存过期时,Mysql是唯一回源,Mysql能承载的瞬时QPS受当时应用总连接数和sql平均响应速度的影响,连接数打满时会给前端返回空会话列表。虽然可以增加POD数量、增大akso proxy连接数、优化sql和索引来作为短线方案,来提升瞬时请求Mysql容量,但是这种短线方案无法加快单次响应速度,mysql响应越来越慢的的问题依然在。另外增加POD数量也会降低发版速度。
单表会线kw。单个大up的会线W条以上,会线亿条会话。单个Up的会话会落到一张表中,每张表都有比较严重的数据倾斜。如果考虑增加分库分表的方案,sql查找条件依然需要用户uid,所以相当于倾斜数据要转移到新的单表,问题没有解决。另外,重新分库分表过程中新旧table增量同步和迁移业务读写流量的复杂度也很大,有比较大的业务风险。
Mysql的规格是48C 128G和32C 64G。由于会话数据量大,Mysql buffer_pool有限,数据比较容易从内存淘汰,然后mysql需要进行磁盘扫描并将需要的数据加载到内存进行运算,加之比较多的磁盘扫描数据,这时的响应一般在秒级别,接口会给前端返回超时错误,会话列表页空白。
为了适配业务发展,Mysql 会线个非聚集索引,如果通过增加索引使用业务需要,需要更大的Mysql资源,且解决不了冷数据慢查询的问题。增加更多索引也会让Mysql写入更慢。
每条私信内容都绑定私信自己的发号器生成的msgkey,即私信内容唯一id,该msgkey包含私信发送时的时间戳(消息ID生成可参阅读《微信的海量IM聊天消息序列号生成实践》)。读写私信内容Mysql之前先从msgkey解析出时间,用这个时间路由分库分表。
私信内容库按季度分库,分库内按月度分表,单表数据量数亿,数据量最大的用户日增私信351.9W条。按照曲率预测,25年全年数据量有近百亿,如果继续按照月度分表,分表规则不适应增长。
当前该Mysql最大写qps 790,特别活动时写qps峰值预计是20k,但是为了保障Mysql服务整体的可靠,单库写流量我们需要控制在3000qps以下,无法满足写入量峰值时的需要。
此外,消息内容表结构包含了群聊、单聊和应援团小助手全部的属性,增加业务使用难度。绝大部分私信内容是单聊的。
这些私信都需要实现发送和触达两条核心链路,四种私信核心链路的代码逻辑和存储耦合在一起,代码复杂度随着业务功能上线而不断增加,熵增需要得到控制。
从微服务这方面来说,实例和存储耦合会带来资源随机竞争,当一方流量上涨,可能给对方的业务性能带来不必要的影响,也会带来不必要的变更传导。
基于对私信现状的论述,可以确定我们要优化的是一个数据密集型 计算密集型,读多写少(首页未读数)、读少写多(会话)场景兼具的系统。
BFF网关吸收上浮的业务逻辑,控制需求向核心领域传导。服务端基于业务领域的能力边界,抽象出单聊、群聊、系统通知、互动通知和消息设置共五个新服务,提升微服务健康度。
1)冷热分离:多级缓存 redis(核心数据有过期)+taishan(有限明细数据)+mysql(全部数据);
用户在首页获取未读数是一个业务域内可以捕捉的事件,通过异步消费这个事件通知服务端创建会话缓存,提高用户查看会话的缓存命中率。鉴于大部分人打开B站并不会进私信,此处可以仅大UP预热。大UP的uid集合可以在数平离线分析会话数据后写入泰山表,这个泰山表更新时效是T+1。
监控UP会话数量实时热点,触发突增阈值时,通过异步链路自动为热点用户主动预热会话列表缓存。
对于ZSET和KV两种数据结构,评估了各自读写性能的可靠性,符合业务预期。业务如果新增会话类型,可以跟本次新增泰山有限明细一样,基于会话类型的具体规则新增泰山Key。
查询redis未命中时会优先回源泰山,考虑到泰山99分位线ms以下,而且Mysql多从实例都能承受来自C端的读请求,所以采用比泰山报错后降级Mysql稍微激进的对冲回源策略。
在泰山出现“长尾”请求时,取得比较好的耗时优化效果。可以使用大仓提供的error group结合quit channel实现该回源策略,同时能避免协程泄漏。整个处理过程在业务响应和资源开销中维持中间的平衡,等待泰山的时间可以灵活调整。
虽然我们重构了新服务,但是老服务也需要保留,用来处理未接入BFF的移动端老版本和web端请求,这些前端在更新会话时(比如ACK)请求到了老服务,新服务需要通过订阅会话Mysql binlog异步更新本服务的redis和泰山。为了避免分区倾斜,订阅binlog的dts任务使用id分区,这样方便的是一条会话在topic的分区是固定的。
为了避免两次请求分别命中泰山和Mysql时给用户返回的数据不一样,需要解决三大问题:
b. 当Mysql一条会话记录在短时间内(秒级)多次更新,要保证binlog处理器不会逆时间序消费同一个会话的binlog,即跳过较早版本的binlog;
这三个问题都要保证最终一致性,具体解决方案是用redis lua脚本实现compare and swap,lua脚本具有原生的原子性优势。dts每同步一条binlog都会携带毫秒级mtime,当binlog被采用时,mtime被记入redis10分钟,如果下一条binlog的mtime大于redis记录的mtime,这条binlog被采用,否则被丢弃。
这个过程可以考虑使用gtid代替mtime,但这个存在的问题是每个从实例单独维护自己的gtid,当特殊情况发生mysql主从切换,或者dts订阅的从节点发生变更,gtid在CAS计算中变得不再可靠,所以我们选择了使用mtime作为Mysql会话记录的版本。
通过消费路线高性能设计保证泰山异步更新的延迟在1秒以内,并在特殊情况延迟突破1s时有效告警。高性能消费路线中,每个库的binlog分片到50个partition,业务提供不低于50个消费pod,单pod配置100并发数,按照写泰山999分位线ms计算,每秒可以消费 50*100*(1000/20)=250000 条,大约线倍,考虑dts本身的max延迟在600~700毫秒,同步泰山和redis的延迟会在700毫秒至1秒以内,符合业务预期。
BFF已经从业务层和平台层将单聊读收件箱独立出来,本次升级主要是从存储做增量解耦 ,存量单聊收件箱的读流量可以访问旧表。 单聊新收件箱存储采用redis+泰山的模式,redis提供热数据,泰山提供全部数据并采用RANDOM读模式,让主副本都能分担读流量。
1)单聊增量数据独立存储,按照单聊业务设计表结构,和群聊、应援团小助手彻底解耦。
2)写Mysql升级为异步化操作,提高写性能天花板,这种异步写Mysql改造不会影响读消息内容的可用性和设计。
3)单聊分库规则升级为月度分库,单库内分表为100张。 群聊、应援团小助手和历史单聊依然使用旧的分库分表规则读写Mysql。
业务需要对增量单聊私信路由分库分表时,先从msgkey先解析出时间戳,找到用时间戳对应的月份分库,然后用msgkey对100取余找到分表。这种方案能达到按时间纬度的冷热数据的分离,同时由于msgkey取余的结果具有随机性,平衡了每张表的读写流量。这样预计2025年单表数据量能从9亿下降到900万。
设计技术方案前设立合适和有一些挑战的目标,但这个目标要控制成本,做好可行性。
设计技术方案的时候,需要清楚现有架构与理想架构的差距和具体差异点,做多个方案选型,并确定一个,这个更多从技术团队考虑。
其次要保证功能在新老架构平稳过渡,保证业务的稳定性。后面持续关注新老架构的技术数据,持续优化,老架构要持续关注它的收敛替换。
IM系统是一个老生常谈的话题,也是融合众多有趣技术难点的地方,欢迎感兴趣的同行交流研讨。
平台声明:该文观点仅代表作者本人,搜狐号系信息发布平台,搜狐仅提供信息存储空间服务。