本文将着重分享目前非常流行的直播平台里的“IM系统”。“板栗直播”IM系统从立项初期,到发展致今,已经成为一个功能复杂,拥有一定稳定性的系统。
我们做IM系统1.0版本时,有以下几个需求:
由于只有一周的时间,第一版的架构搭建得非常简单:
服务端由PHP和NodeJS组成。PHP负责用户账户,注册和登录。NodeJS负责长连以及进出房间和推送,做成简单的交互。
第一个版本的前端服务器的结构非常简单,功能很少,仅仅负责联接客户端,负责所有客户端的联接。为了保证H5也可以使用,因此在长连上选择了WebSocket。
为了确保客户端演示的时候,万一崩溃了也不能被察觉,我们使用PM2守护了Node进程。同时让客户端同学做了个自动断线重连。
然后登录流程是这样的,先从PHP获取一个token,然后带着这个token和Node建立连接,Node会在内网向PHP验证token是否有效,然后建立连接。
然后广播方面,使用了我称作组的形式进行广播,所有的房间和私信都是通过组的形式进行广播。
首先,用户在房间里发言的时候,实际上是往特定组发送广播。组的名字是用特定字符开头来归类的。
举例来说,需要向10000房间发送聊天信息时,实际上是向r:10000这个组发送信息。此时服务端会遍历该组里所有连接,并发送消息。
当需要进行全服广播的时候,我们使用一个写死特殊的0号房间,转换成GID也就是r:0发送消息。这将遍历所有连接,向他们发送消息。
接下来是推送私聊,这里我为每个用户都设置了一个私有的Gid,这样发送私信的时候也相当于发送了广播,服务端可以用相同逻辑处理。打个比方向UID 20000用户发送私信,相当于向u:20000广播。以后扩展成多进程多主机模式时,也不必知道这个用户目前在哪台服务器上,推送私信就会很方便。
在这个设计中,一位用户是允许加入多个组的。就像群聊一样,一开始就使用这样的设计,以后可以很容易的进行扩展。
由于采用了单点连接的形式,并不需要每次进房间都建立新连接,客户端只要首次进行连接,之后发送请求就可以进出房间,也节省了客户端的开发工作。
2.0版本需求:
当1.0版本完成之后,接下来就是要开发2.0版本。这个版本里增加了一些必要的需求。
首先,这是一个内测版本。在内测版本中会邀请用户使用,并进行测试。期间可能由于用户数增多,也可能因为我们代码本身的原因或是bug造成服务器压力增加,此时会有扩容方面的需求。
其次是要增加广播优先级的功能,这个很简单,礼物肯定是比普通信息优先级更高的。
最后是特定消息的回执发送,这个我们目前也还没有实现,但是需求一直是在的。
上图中可以看到模块数增加了,但是总的来说客户端需求并没有变化,基本流程的连接流程仍然是先问PHP要token,再去连接WS。由于我们允许把WS部署在多个主机上,在这中间增加了一步就是访问LB获得WS地址。LB会轮询地告知客户端本次应使用哪个WS,并把IP返回给客户端。确保所有WS的压力是平均的。
另外还有一处增加的功能是,由于WS已经分离到多个进程了,那么需要一个地方可以用来处理广播。这边我们选择了Redis的进行广播。
同时另外还部署了一台Redis专门用来保存在线列表等数据。
上图是一个带有优先级的推送消息。
大家可注意到,我们为每个组的GID增加了一个后缀,这个后缀是用来区分优先级的。
比如说用户进入房间10000,那么我会把他放到2个组,分别是r:10000._和r:10000.n。
同时Node也会向Redis订阅两个同名频道,此时服务端就可以从2个频道收到推送消息,分别是_(普通)和n(优先)。
后端会有聊天信息要推送,会根据优先级,是使用普通频道还是优先频道进行广播。
Node这边会开一个主循环,比如设置12fps,每一帧会优先转发优先组(频道)中的消息,然后才是普通消息,根据当时压力,将会选择抛弃普通消息。
3.0版本需求:
到了3.0版本时,需求越来越多,需要拆分。线上有时候会有bug和做活动,会有热更的需求。Redis广播有可能出现瓶颈,在这之前需要需要更好的广播性能,同时还需要优化与其他服务器之间的通信。最后还需要需要有一个简单的日志搜集器。
这是3.0版本的架构。大家可以看到下面有大量的服务了。其实这些服务原来是在WebSocket里面的,现在把它拆成不同的进程。
客户端连接这块没有变化,客户端仍然需要问PHP要token,问LB要地址,最后再连接。
这个版本的WS功能上更加单一,只负责消息转发。在这个版本里,我们做了一套RPC框架,用来方便调用内网各服务的接口。
增加了日志搜集器和API的服务。以及IM系统(私聊)。用户之间可以聊天和查看聊天记录等。
总的来说,功能上没有特别大的变化,尤其是接口方面是向前兼容的。
下面介绍一下RPC服务框架:
我们的RPC框架是基于ZeroMQ的。利用他的里的Dealer和Router进行消息的收发。ZeroMQ 自带自动重连和负载均衡功能,这方面也不用太操心,节省了开发时间。数据交换格式是JSON,明文传输,调试也会很方便。
ZeroMQ性能
# local_thr tcp://*:5555 100 10000000
message size: 100 [B]
message count: 10000000
mean throughput: 1097051 [msg/s]
mean throughput: 877.641 [Mb/s]
还有就是我们不再使用Redis做广播,而改用ZeroMQ的Pub和Sub做广播服务。这样广播性能就有了很大提升。
IM系统4.0版本的需求:
到了这个阶段,我们遇到了一些个别地区网络较慢的问题。
我们把WS和LB放到了代理服务器后面。LB可以根据不同运营商用户,返回代理了的WS地址。
同时还优化了RPC服务,我们为RPC服务增加了路由器,所有的Worker都隐藏在他后面,这样客户端调用的时候并不需要知道具体Worker的地址。Worker的更新重启也不会影响其他客户端。
广播服务也得到了业务和广播分离,同时集群化。
声明:本文来自「又拍云主办的Open Talk——直播平台架构与优化实践」的演讲内容整理。PPT、速记和现场演讲视频等参见“UPYUN Open Talk”官网。
嘉宾:张皓聪,恺英网络程序经理,2010年加入恺英网络,先后负责过多款手游页游项目,对NodeJS和ZeroMQ有深入研究。目前负责恺英网络“板栗直播”IM系统的相关开发工作。
责编:钱曙光,关注架构和算法领域,寻求报道或者投稿请发邮件[email protected],另有「CSDN 高级架构师群」,内有诸多知名互联网公司的大牛架构师,欢迎架构师加微信qianshuguangarch申请入群,备注姓名+公司+职位。