如何设计一个几十万在线用户弹幕系统
一、需求背景
现在无论是直播还是电视剧,我们都可以看到上面慢慢的弹幕,如果有一天公司自己要做一个这样的满足十几万用户在线的弹幕系统,我们该如何去设计呢
二、技术选型
弹幕跟我们平时做的系统不太一样,平时做的最多的就是客户端发起请求,也就是数据都是由客户端推送到服务端,但是弹幕是由服务端推送给客户端的。比如我们现在在看直播,很多用户都会发送弹幕,然后每个在线的用户都可以收到这条弹幕信息。这时候我们有二种方案
2.1 客户端轮训
也就是客户端不停的去服务端去拉取数据,但是会有一个弊端,因为我们每次看电视剧的时候,看一遍一条弹幕只会显示一次。如果不停的去轮训,会有很多无效的请求,而且不停的去轮训无疑会给服务器造成很大的压力,因为你客户端少,如果现在有十几万的客户端同时在线,那么服务器每隔一段时间就会有几十万次的查询压力。
2.2 WebSocket 主动推送
随着现在 IM 系统的成熟,WebSocket 也越来越被重视,WebSocket 在当服务器收到消息之后,可以主动将消息推送给客户端。
三、弹幕初始架构
在开始的时候,因为用户不多,我们只有一台服务器,也能满足需求,但是试想一下,如果某一天用户增多了,你这一台服务器真的顶得住十几万的用户来访问吗???
既然这时候一台服务器压力太大,那我们就部署多台服务器,将压力进行分摊,这样每台服务器的压力就不会那么大。
但是这时候单台服务器压力是解决了,那推送消息给每一个用户就做不到了,我们知道 WebSocket 在推送消息的时候,会先拿到这个用户的 Session,然后通过这个 Session 将消息推送给这个用户,但是用户 Session 都是保存在我们本地服务中的。比如这个用户 A 的 Session 在服务器 A 上,用户 B 在服务器 B 上,那么你怎么拿到用户 B 的 Session 呢?
很多人会说,把所有用户的 Session 都保存到 Redis 中不就可以了,其实这样也不是不行,但是如果你现在有几百万,几千万的用户 Session 呢?
所以我们可以使用消息中间件或者 Redis 的发布订阅模式,比如使用消息中间件 RocketMq,有一个用户发送了一条弹幕,那么就发送一条消息到 MQ 中,所有服务都接受到这条消息之后,然后就可以拿到本地所有用户的 Session,再进行发送。
四、弹幕架构演进
不知道大家有没有想过这样一个问题,假设我们现在有 10 台服务器,每台服务器有一万人,这时候,一个人发送一条弹幕,那么每台服务器 WebSocket 都要推送一万次消息到客户端,如果这时候有一万人发送了弹幕呢?那么 WebSocket 是不是就要推送 1 万 * 1 万次数据呢。在这种高并发的情况下,WebSocket 会因为发送的太频繁导致各种各样的问题,比如与客户端断开连接等。
所以第二个难点就在这了
现在我们把所有需要推送的消息都发送到 MQ 中,然后在 MQ 中将消息推送给客户端。为什么要这么做呢?
在 MQ 中我们可以进行限流,这样 WebSocket 就不用频繁的去操作了。尤其类似于弹幕这种业务,也不是说要求实时的。一般我们发送一条弹幕,都会过个几秒钟我们自己才能看见,所以我们可以根据自己服务器的性能来决定消费速度。
五、弹幕存储
用户发送的弹幕最终都是要进行持久化存储的,就像腾讯视频一样,无论你什么时候去看,那些弹幕都还在。那么弹幕存储时机是什么时候呢???
一般都会选择在用户发送一条弹幕之后就存储这条弹幕,如果用户发送一条弹幕我们就同步的将这条数据存储到 MySql 中,那么这时候有几十万的弹幕该怎么办? 这时候数据库的压力就很大了。因为弹幕这种数据丢个几条对我们业务没有任何影响,所以我们可以选择异步存储,比如将要保存弹幕信息发送到 MQ,MQ 异步的将消息存储到 MySql 中即可
六、弹幕查询
对于弹幕查询,我们后续可以将数据同步到搜索引擎 Elasticsearch 中,后续的查询就直接查 ES 就好,就不用查数据库,。避免对数据库造成压力
七、总结
对于弹幕系统来说,难点就在于二个,第一:对于大量消息推送到客户端,第二:就是消息的异步存储了。
对于这个方案还有什么不足的地方,欢迎大家提出,一起进步
作者:我是小趴菜
链接:https://juejin.cn/post/7223055227465056314
来源:稀土掘金
评论