手把手带你搭建弹幕系统.docx
文本预览下载声明
UCLoud中国云三强:
手把手带你搭建弹幕系统
16 年是直播浪潮兴起的元年,许多互联网公司的业务都开始涉足直播内容模块。我目前所在公司接手的首份工作,就是直播业务中的弹幕系统优化。随着公司直播业务的变化,弹幕系统从起初的版本到后来优化了三四个版本,这个过程大概持续了一年的时间,本文将从我司早期的弹幕系统开始给大家介绍整个更新过程的“血与泪 ”。
早期弹幕系统
一、基本状况
1.由 PHP + Gateway 框架编写。
2.所有的 Client ID 存放在 Redis 里面。
3.起初由三台机器挂载在 LVS 系统后方提供服务。
4.使用多进程的方式,开启多个 worker 进程来处理消息传递内容。
二、存在的问题
1.内存占用量巨大,单机(4 核 8 G 配置)承受 500 左右的 Client 就会达到内存上限。
2.每次发送消息的时候,每台机器都需要从 Redis 里面拿取对应房间的所有 Client ID;并发高时,Redis 的单进程处理效率和内网带宽就成为瓶颈 。
3.单机的并发处理能力被消息处理的 worker 进程数量限制。同时开启过多的进程,也是对系统资源的格外浪费。
4.单房间超过 2000 人的时候,消息的延迟有可能会达到 1 分钟左右,这是极其严重的问题。
三、临时改造
由于需要解决的问题比较紧迫,所以快速做了一些逻辑上的改变和业务层面的取舍:
1.对 Redis 的实例进行了拆分,使用了双机,单机 4 实例的方式,分散了 Redis 的压力。
2.对消息处理 worker 进程的逻辑做了一些修改,限制单位时间内进行广播的消息数量,多余的消息会被丢弃 。
3.对于已经完成了直播进入点播状态的房间,额外启用了另外一套弹幕系统来进行分流。
4.单个房间切成多个房间进行消息处理。
四、改造之后的效果
1.Redis 压力大幅度降低;
2.单机 IO 性能压力降低;
3.同样数量的机器,可以承载更多的直播房间个数。
但是,根本问题并没有得到解决。在临时解决压力问题之后,我们需要花一些时间来重新对弹幕系统进行分析,按照分析后的需求,对新的弹幕系统进行重构。
新的弹幕系统
一、新弹幕系统面临的挑战
1.单房间人数较高,依照我们公司直播情况,单房间 5 - 10 万人同时在线是会出现的。
2.由于直播内容等情况造成的某时间段用户暴涨。
3.需要尽可能实时到达,延迟过高的话会大大降低互动的实时性。
4.每一条消息,都要递送大量的长连接。
5.大量长连接的维护机制。
6.在运营的过程中,需要处理用户黑名单、IP 黑名单、敏感词等需求。
二、新的弹幕系统需求
1.由于内存的管理对于 PHP 来说算是一个短板,对于大并发且长时间稳定不需要经常更新维护的系统来说,并非很好的选择,因此选一门合适的语言是必须的。
2.分布式支持,可以快速的横向扩展,单房间人数可以支持到十万级别。
3.可以方便快捷的对系统进行第三方消息的发送(例如礼物信息、系统通知等)。
4.尽量使用本地内存管理来记录房间内客户端连接,剩下大量的数据交互和查询时间。
5.并发支持消息广播,提高广播效率。
三、新弹幕系统版本的改造方法
1.选择当前正红且对高并发支持良好的 Golang 作为开发语言。
2.使用开发语言进行客户端连接的管理,且每台机器只管理自己收到的连接请求。
3.使用并发的房间内广播逻辑,同时对多人进行广播。
新弹幕系统改造的相关经验
下面先对一个模块细节进行分析,然后进一步分析模块上层的调度逻辑。
一、房间管理
type RoomInfo struct { ? ?RoomID ? ? ? ? string ? ? ?//房间ID ? ?Lock ? ? ? ? ? *sync.Mutex //房间操作锁 ? ?Rows ? ? ? ? ? []*RowList ?//房间多行Slice ? ?Length ? ? ? ? uint64 ? ? ?//当前房间总节点数 ? ?LastChangeTime time.Time ? //末尾一次更新时间 } type RowList struct { ? ?Nodes []*Node //节点列表 }
由于每个房间都有自己的 ID,客户端建立连接之后,就会被放到一个大厅房间里面。接着,客户端自己提交 RoomID 上来,连接会被重新连接到对应的房间里面。 每个连接在建立之后,都会被包装成一个 Node,放到 Rows 里面。
type Node struct { ? ?RoomID ? ? ? string ? ?ClientID ? ? int64
显示全部