当前位置: 首页 > 科技观察

从百万到亿:EMQX5.0新架构的优缺点

时间:2023-03-13 00:39:56 科技观察

1.Mnesia:Erlang语言的分布式数据库在EMQX5.x版本之前,集群数据存储使用实时内置的Erlang/OTP分布式数据库管理系统——Mnesia。Mnesia是用Erlang语言实现的,与Erlang紧密耦合,这也使它独一无二。它几乎把Erlang变成了一种数据库编程语言。Mnesia可以说是为用Erlang编写的工业级电信应用程序而设计的,并提供支持高容错电信级系统所需的通用功能。它试图解决典型电信系统所需的所有数据管理问题,并具有传统DBMS通常不具备的许多功能。它提供的特性主要包括:快速实时键值查找;复杂的非实时查询;分布式数据支持;高容错性;复杂的数据对象。Mnesia一般支持两种数据访问模式:本地模式和远程模式。本地模式采用全连接点对点复制模式,即节点中的数据表会被复制到集群中的所有节点;而在远程模式下,当要访问的表没有本地副本时,会通过RPC调用读取远程数据表。具有数据表副本的节点。本地方式访问的缺点是集群扩展性差,有裂脑风险,但优势也很明显,因为集群中的每个节点都有集群中的全量数据,所以可以使用本地查询来提高检索效率。与远程模式下的网络运行相比,本地读取数据的延迟比远程模式下的网络延迟小几个数量级。另外,这种实现方式还可以提高集群的分布式容错能力。只要集群中还有存活的节点,集群数据就是完整的、安全的。所以在EMQX的早期实现中,默认使用的是本地模式。尤其是在消息分发中,通过本地查询Mnesia数据库中的路由表数据,快速定位到消息要投递的节点,可以实现个位数毫秒级的高效低延时的消息分发操作。2.Mnesia的缺点:复制带来的开销上面提到,由于Mnesia集群采用全网状连接架构,集群中的每个节点都会与其他所有节点建立连接,每个节点产生的交易也会被复制到集群中的所有节点。这导致集群整体可扩展性差:首先,集群每增加一个节点,集群数据同步的开销也会增加,网络问题导致集群脑裂的风险也会增加。其次,集群中的每个节点都必须能够承载全量的集群数据。与Mnesia经常将数据存储在内存中相比,服务器资源的投入也会随着集群规模的扩大而增加。配置和性能要求会越来越高。集群节点间的数据复制成本和服务器资源投入这两个问题一直是限制集群可扩展性的核心问题。Mnesia网状拓扑3.Mria:从全网状到单复制为了解决Mnesia全网状复制带来的问题,EMQX5.x版本引入了一个新的数据层解决方案实现——Mria。Mria对Mnesia进行了封装,其核心诉求是在实现数据本地读写的基础上,尽可能减少集群节点复制的开销。Mria将原来的全网状复制Mnesia节点扩展为两个不同角色的节点——核心节点(Core)和副本节点(Replicant)。核心节点的行为类似于传统的Mnesia节点。它仍然采用全网状复制模式,所有核心节点之间的交易仍然会被复制到其他核心节点。复制节点不直接参与Mnesia事务处理,而是连接到集群中的一个核心节点,被动地从核心节点复制数据更新。为此,核心节点同时还有另外一个重要的工作,就是负责对自己连接的所有副本节点的数据处理。由于复制节点不再参与集群中事务的同步,只有少数核心节点会实时同步事务,复制节点只复制对应核心节点的数据,所以这种实现方式可以具有完整的复制节点中的集群数据量在实现高效本地数据检索的前提下,还可以减少整个集群的事务同步开销。借助复制节点的特性,当集群需要连接更多的设备时,只需要相应地扩大复制节点的数量,这些节点就可以承载设备连接,而不会直接增加核心节点写入的延迟操作,从而达到扩大集群规模的效果。Mria单复制拓扑架构,但Mria架构的实现并不是灵丹妙药。虽然可以解决全网状复制带来的数据同步问题,但是仍然无法解决集群中所有节点必须承载全量数据的问题。另外需要特别注意的是,为了提高Mria架构的复制效率,EMQX官方在Erlang/OTP实现的基础上引入了一种叫做post-commithook的实现。如果要应用新的Mria架构,需要使用带有此补丁的Erlang/OTP库,否则集群会自动降级到Mnesia实现模式。遗憾的是,到目前为止,这个新特性还没有被合并到官方的Erlang/OTP仓库中,需要开发者自己用这个补丁构建依赖库。PR:mnesia:添加post-commithook#59264.AMQ2.0:基于角色的路由分发AMQ是中国移动智能家居运营中心基于开源EMQX开发的物联网连接中间件。为了增加集群的可扩展性,我们在2.0版本引入了Mria开源实现的新特性,解决了集群节点复制的开销问题。同时,为了解决所有节点都需要承载全量集群数据的问题,我们设计了集群数据复制的新实现——连接分发引擎:一种基于节点角色的订阅/复制的路由分发机制.路由数据是物联网连接集群中的核心数据。它存储了设备订阅主题和集群节点之间的映射关系。发布消息时,根据消息主题信息搜索所有匹配的节点,用于集群内节点间的消息分发。在EMQX的实现中,路由数据存在于集群的所有节点上。客户端的主题订阅数据只存储在连接所在的节点上,用于向节点内的客户端派发消息。当客户端连接到集群中的某个节点订阅新的主题时,会产生一个路由数据,最终会同步到集群中的所有节点,每个节点都可以通过本地找到任意主题对应的订阅节点查询列表。当客户端发布消息时,连接所在节点会根据消息主题检索路由数据,获取所有订阅节点的信息,然后将消息分发给这些节点。Mria实现的一个问题是集群中的许多节点复制了它们不需要的路由数据。想象这样一个场景,一个智能门锁和一个智能台灯分别连接到集群中的NodeA和NodeB,分别订阅主题TopicA和TopicB。由于EMQX实现的特点,两个节点都会存储一个包含TopicA和TopicB的路由数据。但是由于门锁和台灯之间并没有直接向对方发布消息,所以对于这两个节点来说,它们都存储了一个永远不会被使用的路由数据。同样,当集群中连接的设备越来越多时,每个节点上都会有大量无用的路由数据记录。这样不仅会增加服务器资源的投入,还会导致查询性能的下降。另外,当有新的节点加入集群时,也会增加数据复制时间,降低节点的访问效率。在AMQ2.0实现的路由分发机制中,每个节点都有一个数据复制角色:DB(Database)、SVC(Service)或者CONN(Connection)。其中只有少数DB角色节点拥有全量的集群数据,承担集群中“数据中心”的角色。DB节点负责根据可配置的订阅策略将路由数据分发到相应的SVC或CONN节点。另外,SVC和CONN节点并不是简单地复制DB节点的所有路由数据,而是根据可配置的角色策略,选择性地复制自己需要的数据。这样,这些节点存储的数据是有限的,不会随着集群数据量的增加而增加,数据仍然以本地查询的形式获取,不会影响消息派发时的数据获取效率。另外,由于不需要同步集群中的全量数据,每个SVC和CONN节点都可以实现快速访问,快速完成现有数据的复制。AMQ基于角色的路由分发拓扑结构5.总结无论是EMQX4.x的Mnesia实现,EMQX5.x的Mria实现,还是AMQ2.0的路由分发实现,目的都是一样的:保证数据读取在保证写入效率的前提下,尽可能扩大集群规模。