如何设计一个弹幕系统?

2024-09-08 17:23:34 浏览数 (3)

常见问题分析

  • 单房间的百万用户同时在线导致的带宽压力
  • 弱网络问题
  • 性能和可靠性
  • 弹幕的及时性比较强以及瞬间大量弹幕

优化方案

  • 业务解耦,服务拆分
  • 本地缓存,优化高并发读
  • 引入限流,优化高并发写
  • 滑动窗口(Ring Buffer)实现无锁读写
  • 短轮询解决弱网问题
  • 优化传输,节约宽带

业务前提

弹幕数据不落表(不存数据库)

直播不支持回放(回放的话要考虑弹幕顺序问题)

解决方案详解

如何解决带宽压力

百万级用户,如果每隔三秒将弹幕显示给用户,又由于为了用户的优化体验,每一次在用户屏幕上的弹幕最少为15条,也就是15条数据3s内展示完给用户

而15条弹幕 http包头的大小保守为3k, 那么每秒的数据大小约为8Gbps,所以我们该如何解决

方案一:response结构简化

去掉不必要的信息,以节省带宽

方案二:http gzip压缩优化

对传输的数据采用压缩,并利用gzip的原理(重复度越高,压缩比越高),比如某个短视频直播的福袋,有时候需要发送弹幕才能参与,这时候会有大量重复的消息发送到服务器,而服务器可以将这些接收到消息拼接的到一起例如:"猴子猴子猴子猴子", 这是将四个人发送的消息拼接到一起了,而gzip压缩时,会将其压缩成"猴子",压缩之后再发送给客户端

连接方式优化

当由于现实环境原因,导致网络不好,TCP连接经常断开时,采用短轮询的方式,如果采用websockets等其他方式的话,可能造成弹幕卡顿, 丢失,延时等情况,考虑到直播业务弹幕交流的重要性,可以采用短轮询的方式

当现实网络没问题,可以采用websocket或者长轮询的方式来解决,这两个的区别是通过websocket双方连接上了,没有特殊情况或者主动关闭,连接不会关闭,而长论询如其名,只不过短论询是响应完数据不论后面是否有新数据就立马关闭,而长论询则是等后面的新数据,或者超过了超时时间才会关闭,因此长轮询的及时性没短轮询好,但是当连接数量多起来时websocket和长轮询更能抗打

缓存优化

定期发起rpc调用,从弹幕服务拉取数据并存到内存中,这样后面的鹅数据只需要从本地缓存读取即可

环形数组结构环形数组结构

这里我们可以采用ring buffer,其数据结构是环形数组,写操作顺时针,读操作逆时针,如果有了解redis数据备份原理的,可能更好理解,对时间片进行分片,将接收的数据的时间`,这样就对应了每一秒有哪些弹幕显示,这种方式不用考虑加锁问题,因为是用户本地缓存(用户手机上的缓存),写操作是单线程的,而读是读取之前的数据,不会与写操作的数据产生冲突,如果读写重合的话,我们可以限制最多只能读取30s之前的数据,这样读写操作操作的数据区域在同一时间内就不会重合,也不会带来线程问题

采用推模式

最新的消息可以直接采用长连接实时推送, 因此用户发送弹幕到消息队列后,消费者做两件事

一是将消息写到redis,(可能有人说redis内存会爆,但是这是弹幕系统,弹幕数据每隔一段时间删除即可)

二是将消息写到弹幕的推送服务器(推模式),推服务器的作用是从 Redis 中获取用户和直播间的订阅关系以及长连接信息用户连接上下文等信息,将数据压缩分批并发推送给用户


参考文章:

之乎者也·-CSDN博客

1 人点赞