咱们在5月正式发布了实时消息(RTM)SDK。在5 月 27 日举行的 Qcon 广州站上,声网 Agora 资深技术架构师吉奇 以《高并发场景下分布式实时信令系统的架构实践》做为话题,分享了 RTM SDK 背后的架构设计经验。算法
如下为演讲实录:服务器
你们好!我叫吉奇,来自声网。如今负责声网RTM 实时信令云服务后台及SDK技术架构设计。此次演讲会按照RTM的系统架构上的分布或子系统的层级关系来展开。网络
首先,RTM 是一个通用的消息系统,主要是为了解决实时场景下信令的低延迟和高并发问题。咱们声网是业务遍及全球的平台,所以在全部的后台设计中,把分区做为一个比较重要的事情来看。目前 RTM 有几个大区域,有美洲、亚洲、东南亚、中国大陆,还有欧洲、非洲几个大区。区与区之间相对独立,每一个区会有跨区传输网络。每一个区之间由三个子系统组成,首先是消息核心(Message Core),还有事件中心(Event Center),最后是应用服务(Application Services)。我会分别讲一下各个子系统内部的架构实现,即消息核心、事件中心、应用服务和跨区网络。session
首先是消息核心,它是目前成熟度最高,也是最复杂的子系统。在该系统里面有几个主要的组件,首先有接入服务器、点对点消息转发服务、频道消息的转发服务、简单的状态管理(包括用户状态和频道状态),还有频道分布状态服务器。架构
在消息核心,全部的服务都是分布式,没有一个单点或者中心式的状况,所以能够保证高可用,而且性能方面能够支持高吞吐量和低延迟。Messaging Core 有一个特色,具备很是大的扩展性,可是它的问题是只支持基本核心的功能,剩下的都要放在其它子系统中。并发
分布式的信息核心有几个优点特性:负载均衡
彻底排除单点故障分布式
接近100%可用微服务
端到端延迟 < 100ms高并发
任何节点均可水平扩展
支持数百万人同频道(无理论上限)
大型活动中支持数百万QPS消息下发
核心功能超高响应
所谓核心功能,目前消息核心支持的功能是点对点消息、频道消息,能够加入频道、退出频道。用户也能够同时加入多个频道,使用一些频道管理的功能,好比获取用户属性、频道状态,能查询频道中有多少人,其余用户是否在线等基本功能。
在此,以点对点消息为例,和你们分享一下扩展性是怎么样作的。首先 SDK 登陆系统的时候,会经过 DNS 来访问咱们的 AP 服务,AP 知道附近的边缘节点 R 的地址,会根据当前的客户端的地理分布,包括边缘节点的负载状况来给 SDK 回一组地址。SDK 在拿到地址以后,能够登陆链接边缘节点,而后发消息。这些消息到达边缘节点后会投递给本区的点对点消息转发节点 F。F 知道本区内全部用户登陆在哪一个边缘节点,这是由本区全部边缘节点 R 上报给转发节点 F 的。图中的 U 是用户在线状态服务器,那么一个用户给另外的用户发消息,有三种状况,第一种状况,对端在线而且在同一个区里面,F 能够直接投递;第二种状况对端在线但在别的区里面;第三种状况对端不在线。在后两种状况中,消息转发服务器 F 不知道该用户的信息,也不知道在哪一个节点上。这时候就能够经过 U 来获取这些用户状态,由于 U 知道全网跨区状况下的用户生命周期,也知道这个用户是否在线,F 去问 U 是否在线,若是在线在哪一个区里面,能够经过跨区投入到别的用户。
这里的可扩展体如今哪里呢?首先,全部的节点都是能够水平扩展的,随着业务量增加,能够增长部署。边缘节点是能够随意增长的,而核心节点 F 和 U 不能作任意的水平扩展,由于他们保留了必定的状态,咱们用了一个一致性哈希的分片方法,因此把全部用户的帐号哈希以后产生一个 32 位的随机数,想象把这些数放到一个环上,每一个服务器各自产生一组随机数,在环上均匀分布。这样全部的消息会被映射到比本身的哈希值小的那一个服务器上面。全部的节点的 partition 都是能够动态地增长和减小的。假如说有一个核心服务器故障或者下架了,那么它能够从新分布到别的服务器上,实际上咱们地消息核心中除了边缘节点R以外还有十几种核心节点,它们都是作了分片的。这就是所谓的可扩展性。
高可用怎么样作呢?首先如上图所示介绍一下频道消息简单的流程。假定边缘服务器收到用户的频道消息,会把该消息投递给 F,F 是点对点消息的转发服务器,它看到是频道消息的话会自动抛给 D,D 专门负责频道消息分发,D 采用是级联的模式,每个区都有一组总的频道消息分发服务器,在每一个数据中心会有一组机房级别的代理。区域级根服务器发消息到机房级别的代理服务器,机房级服务器往该机房全部的边缘节点 R 转发,这样能够保证在超大频道下面的性能。如今有一个问题,以前我说了 U 是保存用户的生命周期的,而频道的生命周期与用户不同,频道不是一个特定的个体。好比说用户要么在中国或美国,不可能同时在中国和美国,但频道能够。尤为当频道比较大的时候,分布会很是广,颇有多是跨区频道,甚至在中国、美国、欧洲都有用户处于同一频道。那么你该怎样获取某频道的用户分布呢?咱们用频道分布服务器 O 来处理。全部的 R 都会在本地频道建立、销毁的时候,把该事件通知给 O。O 把频道分布的信息告诉频道消息转发服务 D,D 会从中得到两个信息,第一个信息是对于某频道来讲,在本区内该频道的用户分布在哪几个边缘服务器上,第二个信息是能够知道该频道是否跨区,若是跨区的话,又是哪几个区域。D 经过第一个信息能够判断在本区投递给哪些用户,经过第二信息能够知道须要经过跨区传输网络投递给哪些别的区域的 D,让它们在别的区域来负责下发。
在这里高可用主要体如今 O 是对等部署的。咱们每一条消息或者每一次状态改变或者每个查询请求都会有一个全局惟一的 ID,这个 ID 由两部分组成,第一部分保证其惟一性,第二部分保证在某一个 session 以内先后的请求有一个单调递增的大小关系。这样的话,从多台对等部署的 O 同步给 D 的频道分布信息,就至关于要保证一个单一来源但多路径的信息同步的一致性问题,咱们是能够经过这个 ID 来作到版本控制和除重从而保证一致的。固然对等部署只是其中一个手段,还有不少别的模式用到不一样的服务上面,好比事件中心的高可用就是由双数据中心主备切换来保证的。但消息核心中的服务通常都是采用的比较激进的对等部署的方式,这样的好处是任何一个服务器挂了都不会有切换的事件,保证服务 100% 可用。
Messaging Core 下面是 Event Center。就像我在开头说到的,Messaging Core 有一个限制,它是靠多重冗余和相对激进的策略来保证低延迟和高可靠的系统,所以不少扩展的功能没有办法作,因此会经过 Event Center 来支持这些扩展功能。
举个例子,好比用户属性是在消息核心中完成的,而频道属性在消息核心中就作不了。由于频道属性和用户属性不同的地方在于,对于某一个用户,他的用户属性只有他本身可以编辑,他是该属性的主人,由该用户的客户端来保证属性的一致性。因此就算在服务端有多重冗余的状况下,该属性也能够达到最终一致。但频道属性不一样。频道里可能同时有多我的在同时编辑频道属性,也可能同时有多我的在读该属性,怎样达到一致性?这里就须要对频道消息的编辑操做有一个统一的来源。但这个来源又不能是单点,不然很容易出故障也很容易成为瓶颈。
所以咱们决定将全部的事件,包括状态改变、消息的投递都统一写到 Event Center 里面。Event Center 分为两个部分,Event Storage 和 Event Queue。咱们的实现原则是传输与状态隔离,数据与索引隔离。传输是 Messaging Core 和跨区传输网络来负责,状态是存在 Event Center,而 Application Services 是消费的状态,这样能够作到传输与状态的隔离。
那什么叫数据与索引隔离呢?对于全部的事件来讲咱们都会把它的 meta data,或者叫事件的 header 放到 Event Queue 里,这样消费者去消费事件队列的话就会很快,而事件的内容自己则放在 Event Storage。我以前说过对于 RTM 的全部消息、事件、查询都有一个ID,这样的话就能创建一个事件 Header - 事件ID - 事件Body 之间的映射。消费者能够经过 Event Queue 创建对事件 Header 的索引,经过这个索引来作各类业务逻辑,而后再经过 ID 来找到对应的事件 Body。好比对于历史消息的条件查询就是这么作的。在这种模式下咱们能够作到好比查询当前在线的全部用户里属性属性知足 "gender:female","age:24" 的用户。
Application Services 是一个微服务的架构,在 Event Center 的支持下能够支持不少的业务逻辑。还包括实时的监控、计费、问题调查、分析等。它的好处是易于开发,咱们经过 Event Center 把传输和事件解耦了,让咱们能够更容易地实现更多的功能。目前已经落地的功能包括频道属性和历史消息,还有不少其余的功能在开发中。
下面讲一下跨区传输网络,它负责全部区域到区域之间的通讯。咱们有去中心化地实时路由计算策略,会根据延迟和负载来动态挑选跨区路由。实际上你发如今不少场景下面,跨境传输是最难的问题,尤为是在教育场景下。例如,老师在东南亚某个地方,学生在国内,他们之间创建链接、收发一些消息的过程当中,稳定性和到达率会遇到不少问题。声网全球有 200 多个数据中心,咱们经过智能路由来进行实时传输,好比中国到菲律宾,当前网络很差的时候,咱们可能会经过新加坡进行中转,若是新加坡到菲律宾好可是到国内很差,咱们会也许会经过国内某个机房先中转到新加坡。RTM SDK 今年上线后,从运营数据来看高峰期的跨洋平均 RTT 是 250ms,该数据已经比较接近实际网络传输延迟。
如上图所示是简化版的跨区传输网络,这个算法有点相似于 BGP 算法。自治域与自治域之间全链接,每一个节点都有本身的路由表,每一个节点会按期广播本身的路由表到别的节点。好比 A 知道到本身到 B、C、D 的延迟是多少,一轮广播以后 B、C、D 就会知道本身若是经过 A,到其余节点的延迟会有多少。各节点会选择延时较短的路线传输。固然,实际策略确定不会这么简单,由于若是全部节点都采用相同策略,流量可能会聚集到某一些节点上去,在流量高峰期时会对这些节点形成冲击。所以咱们有一套很复杂的策略来进行负载均衡。