从1.0到4.0,详解直播平台“IM系统”搭建全攻略

更新时间:2016-12-28 10:46:22点击次数:740次

1.0版本——速度要快

我们做IM系统1.0版本时,有以下几个需求:

  • 一周出Demo
  • 差不多能用
  • 演示的时候不能突然崩溃
  • 万一崩溃了不能被发现
  • 性能无所谓
  • 总之,要快速做出来

由于只有一周的时间,第一版的架构搭建得非常简单:

服务端由PHP和NodeJS组成。PHP负责用户账户,注册和登录。NodeJS负责长连以及进出房间和推送,做成简单的交互。

第一个版本的前端服务器的结构非常简单,功能很少,仅仅负责联接客户端,负责所有客户端的联接。为了保证H5也可以使用,因此在长连上选择了WebSocket。

图片描述

IM系统1.0版本架构图

为了确保客户端演示的时候,万一崩溃了也不能被察觉,我们使用PM2守护了Node进程。同时让客户端同学做了个自动断线重连。

然后登录流程是这样的,先从PHP获取一个token,然后带着这个token和Node建立连接,Node会在内网向PHP验证token是否有效,然后建立连接。 
然后广播方面,使用了我称作组的形式进行广播,所有的房间和私信都是通过组的形式进行广播。

图片描述

向组推送消息

首先,用户在房间里发言的时候,实际上是往特定组发送广播。组的名字是用特定字符开头来归类的。

举例来说,需要向10000房间发送聊天信息时,实际上是向r:10000这个组发送信息。此时服务端会遍历该组里所有连接,并发送消息。

当需要进行全服广播的时候,我们使用一个写死特殊的0号房间,转换成GID也就是r:0发送消息。这将遍历所有连接,向他们发送消息。

接下来是推送私聊,这里我为每个用户都设置了一个私有的Gid,这样发送私信的时候也相当于发送了广播,服务端可以用相同逻辑处理。打个比方向UID 20000用户发送私信,相当于向u:20000广播。以后扩展成多进程多主机模式时,也不必知道这个用户目前在哪台服务器上,推送私信就会很方便。

在这个设计中,一位用户是允许加入多个组的。就像群聊一样,一开始就使用这样的设计,以后可以很容易的进行扩展。

由于采用了单点连接的形式,并不需要每次进房间都建立新连接,客户端只要首次进行连接,之后发送请求就可以进出房间,也节省了客户端的开发工作。

2.0版本——更高的可用性

2.0版本需求:

  • 内测版本
  • 支持扩容
  • 更高的可用性
  • 礼物信息比聊天优先级高
  • 特定消息要发送回执

当1.0版本完成之后,接下来就是要开发2.0版本。这个版本里增加了一些必要的需求。

首先,这是一个内测版本。在内测版本中会邀请用户使用,并进行测试。期间可能由于用户数增多,也可能因为我们代码本身的原因或是bug造成服务器压力增加,此时会有扩容方面的需求。

其次是要增加广播优先级的功能,这个很简单,礼物肯定是比普通信息优先级更高的。

最后是特定消息的回执发送,这个我们目前也还没有实现,但是需求一直是在的。

图片描述

IM系统2.0版本的架构

上图中可以看到模块数增加了,但是总的来说客户端需求并没有变化,基本流程的连接流程仍然是先问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版本需求:

  • 业务逻辑越来越多,需要拆分
  • 需要支持热更
  • 更好的广播性能
  • 优化与其他服务的通信
  • 日志系统
  • 部署脚本

到了3.0版本时,需求越来越多,需要拆分。线上有时候会有bug和做活动,会有热更的需求。Redis广播有可能出现瓶颈,在这之前需要需要更好的广播性能,同时还需要优化与其他服务器之间的通信。最后还需要需要有一个简单的日志搜集器。

图片描述

IM系统3.0版本架构

这是3.0版本的架构。大家可以看到下面有大量的服务了。其实这些服务原来是在WebSocket里面的,现在把它拆成不同的进程。

客户端连接这块没有变化,客户端仍然需要问PHP要token,问LB要地址,最后再连接。

这个版本的WS功能上更加单一,只负责消息转发。在这个版本里,我们做了一套RPC框架,用来方便调用内网各服务的接口。

增加了日志搜集器和API的服务。以及IM系统(私聊)。用户之间可以聊天和查看聊天记录等。

总的来说,功能上没有特别大的变化,尤其是接口方面是向前兼容的。

图片描述

通过RPC中转的推送消息

下面介绍一下RPC服务框架:

我们的RPC框架是基于ZeroMQ的。利用他的里的Dealer和Router进行消息的收发。ZeroMQ 自带自动重连和负载均衡功能,这方面也不用太操心,节省了开发时间。数据交换格式是JSON,明文传输,调试也会很方便。

ZeroMQ性能

  • E5-2630,8G 内存,千兆网卡的测试结果

# 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做广播服务。这样广播性能就有了很大提升。

4.0版本——更高要求

IM系统4.0版本的需求:

  • 广播和业务分离
  • 优化PRC协议
  • 针对个别地区进行网络优化

到了这个阶段,我们遇到了一些个别地区网络较慢的问题。

图片描述

我们把WS和LB放到了代理服务器后面。LB可以根据不同运营商用户,返回代理了的WS地址。

图片描述

RPC服务路由

同时还优化了RPC服务,我们为RPC服务增加了路由器,所有的Worker都隐藏在他后面,这样客户端调用的时候并不需要知道具体Worker的地址。Worker的更新重启也不会影响其他客户端。

图片描述

广播服务也得到了业务和广播分离,同时集群化。

  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息
  • 项目经理 点击这里给我发消息