女主宣言
在体验移动直播应用的时候,想必大家都被满屏的"6666"刷过屏。这种直播当中的“弹幕文化“和社交应用场景可谓是直播领域不可或缺的一部分。每当一个“人气网红”在直播间大秀才艺的同时,这些恐怖的消息量带来的服务端压力可想而知。今天小主就为大家分享一篇来自花椒直播团队总结的聊天服务Qchat的架构演进。
PS:丰富的一线技术、多元化的表现形式,尽在“
HULK一线技术杂谈
”,点关注哦!
花椒直播初期的长连接push服务、聊天弹幕服务,使用的是公司内部其他团队早期开放的服务,在经过了通讯录、手机助手、搜索、摄像头等众多业务线的历练后,服务也趋于稳定。但是随着团队的不断变化、人员的变化,以及业务线长期的迭代后,基于erlang的服务端负载越来越重。随着花椒直播用户体量的增长,为了更好的定制化支持业务的“聊天社交”场景,服务端的重构也逐渐提上了日程。
-
因为公司内部几个大的高并发场景使用的开发语言基本是Go或者 erlang,相应的资源储备也比较完善。
-
Go是Google于2009年发布的编程语言,虽然出现得比较晚,但在公司内部也已经有手机助手长连服务等上亿量级的高并发场景应用,有着很强的Go语言开发技术积累。
-
erlang 是一个历史相对悠久,偏小众的语言,它在高并发、热升级等方面有着先天优势,比较著名的是
WhatsApp服务端深度使用了erlang开发。但因为旧的聊天系统基于erlang系统开发,长期的业务迭代导致基础库方面较弱,比如rpc和日志库,性能及稳定性稍差,再加上erlang先天对http,
unicode, json等支持较差。使用erlang对系统重构相对工作量会更大,因此决定使用Go语言。
erlang系统(简称谈谈)因为支持过的业务场景比较多,各个功能模块化部署,在花椒业务场景中使用到的组件及架构如下。
相应的部署方案如下:
-
可以看到msgrouter承担着长连接入,协议解析和转发,以及与后端session,mongo agent等的直接交互,是主要的逻辑功能实现层。
-
srm属于服务转发,根据长连接service层协议转发到对应的服务,如聊天室,组群等。但花椒目前只用到聊天室功能,因此srm只需转发聊天室相关协议。
-
distribute负责消息派发,所有业务的消息,包括push,私信,聊天室消息最终都由distribute分发到msgrouter,最终通过长连接发送给用户,由于存在聊天室消息一对多的转发,这里的CPU负载相对较重,线上的高峰时的场景也是如此。
参考谈谈架构设计经验,以及Go和erlang语言的特性,新的系统(简称QChat)架构主要考虑以下几点:
-
完全兼容客户端协议,以及业务接口,两套系统对业务和客户端完全透明
-
长连接入层尽可能轻,不涉及主要业务逻辑,避免业务上的需求频繁全量升级长连接入
-
同一组件对业务统一提供接口,内部不按功能模块划分,降低维护成本
-
统一封装redis,mongo,mysql等存储访问的业务逻辑
-
配置动态化
按照设计方案,gateway组件只负责长连接入和数据转发,协议解析和逻辑处理都放在router层,同时router还负责聊天室消息一对多的转发。saver组件封装所有业务的存储访问的操作(包括redis, mysql 和mongo),存储资源也由自己维护的统一迁移到HULK平台,使我们可以更集中在业务优化上。center 组件提供统一的对业务接口。
QChat 线上部署方案如下:
得益于Go语言的开发效率及之前团队的技术积累,我们在4周左右的时间重构完成QChat系统,但真正的挑战是如何平滑过渡,最终全量切到新的系统上。
由于在业务的HTTP接口和长连的私有协议上新旧系统完全兼容,我们设计了如下的切换方案,在初始阶段,QChat作为子系统,接受谈谈系统转发过来的所有业务请求,对于需要落存储的,由谈谈系统去写,QChat只转发通知到自己的集群。对于业务查询类接口,由谈谈系统请求一次QChat,并和自己集群数据合并后返回给业务调用方。长连接入则通过域名解析,LVS等灰度放量QChat直至100%。之后通知各个业务切换业务接口到QChat。通过以上步骤,做到了系统升级对客户端、用户完全无感,对业务使用方基本透明。
在谈谈系统和QChat设计初期,我们考虑到了对直播间消息做降级及流控,但实现方案比较简单,例如对消息做等级区分,对用户付费的消息(如礼物、飘屏等)不做降级,而对一些用户入场,普通聊天等消息在量大的时候按比例丢弃,保证下发消息量不会超出客户端承载能力。
该降级方案在初期基本满足使用需求,但在大型活动,如奥运张继科开播时,数十万用户在一分钟内涌入直播间,礼物、飘屏等付费消息全直播间飞,导致降级策略失效。
根据统计,活动期间每秒钟的消息上行瞬时最高峰超过10,000条,经过简单流控后,下发客户端每秒100条。每条消息平均有500字节,如果直播间在线用户有1,000,000人,服务端机房出口的消息带宽需要400Gbps!
我们根据花椒的实际场景,对各类消息按展示位置做了类别区分,分为以下几大类,同时,根据计算统计每一类消息展示时长:
-
faceU礼物
-
连发小礼物
-
飞屏
-
弹幕
-
PNG大礼物
-
控制消息
-
进场特效
-
其它消息
服务端架构:
在服务端架构上,增加了coordinator和dispatcher组件。 客户端sdk在连接gateway之前会先请求dispatcher,dispatcher根据各IDC机房及服务器负载将其分配到相对较闲的gateway上。
coordinator的作用是所有发到直播间的消息都会被缓存到该组件的内存中,根据展示时长,缓存一定的时间。同时因为整个集群的部署已经达到20多个IDC机房,coordinator也按机房对直播间消息做了写扩散,理论上连到不同机房的用户看到的消息会不完全一样,这样也保证了每个用户的消息基本都能被其他用户看到。
同时coordinator组件会每秒按机房收集所有gateway的出口流量,如果发现某个IDC出口带宽不足时,会自动对发送的消息量按比例下调。后台也会以仪表盘的形式实时显示各个机房的出口流量,运维人员也可以根据该数据手动调整用户连接。
通过这种方式,实现了在有限的消息数量内保证客户端各类消息展示充足,在不影响用户体验的基础上又不至于有大量的消息积压,引起客户端CPU和网络负担。