Mria + RLOG 新架构下的 EMQX 5.0 如何实现 1 亿 MQTT 连接

简介: 大规模分布式物联网MQTT消息服务器EMQX的5.0 版本在发布前的性能测试中达成了1亿 MQTT 连接。本文将对使EMQX水平扩展能力得到指数级提升的全新底层架构进行详细解析,同时帮助大家理解在不同的实际应用场景中如何选择合适的部署架构,实现更加可靠的设备接入与消息传输。

引言: 单集群 1 亿 MQTT 连接达成

不久前,大规模分布式物联网 MQTT 消息服务器 EMQX 发布了 5.0 版本。这一最新的里程碑版本采用新的后端存储架构 Mria 数据库,并重构了数据复制逻辑,因此 EMQX 5.0 水平扩展能力得到了指数级提升,能够更可靠地承载更大规模的物联网设备连接量。

在 EMQX 5.0 正式发布前的性能测试中,我们通过一个 23 节点的 EMQX 集群,全球首个达成了 1 亿 MQTT 连接+每秒 100 万消息吞吐,这也使得 EMQX 5.0 成为目前为止全球最具扩展性的 MQTT Broker。

本文将对使 EMQX 水平扩展能力得到指数级提升的全新底层架构进行详细解析,帮助大家理解 EMQX 5.0 集群扩展的技术原理,以及在不同的实际应用场景中如何选择合适的部署架构,实现更加可靠的设备接入与消息传输。

100 millions MQTT connections testing result

100 million MQTT connections testing result

测试详情可参考: https://www.emqx.com/zh/blog/reaching-100m-mqtt-connections-with-emqx-5-0

4.x 时代:使用 Mnesia 构建 EMQX 集群

Mnesia 介绍

EMQX 4.x 版本存储采用的是 Erlang/OTP 自带的分布式数据库 Mnesia ,它具备以下优点:

  • Embedded: 和 MySQL、PostgeresSQL 等数据库不同,Mnesia 和 EMQX 是运行在同一个操作系统进程的(类似于 SQLite)。因此 EMQX 可以以非常快的速度读取路由、会话等相关信息。
  • Transactional: Mnesia 支持事务且具有 ACID 保证。而且这些保证是针对整个集群所有节点生效的。EMQX 在数据一致性很重要的地方使用 Mnesia 事务,例如更新路由表、创建规则引擎规则等。
  • Distributed: Mnesia 表会复制到所有 EMQX 节点。这能提高 EMQX 的分布式的容错能力,只要保证一个节点存活数据就是安全的。
  • NoSQL: 传统的关系型数据库使用SQL与数据库进行交互。而 Mnesia 直接使用 Erlang 表达式和内置的数据类型进行读写,这使得与业务逻辑的整合非常顺利,并消除了数据编解码的开销。

在 Mnesia 集群中,所有节点都是平等的。它们中的每一个节点都可以存储一份数据副本,也可以启动事务或执行读写操作。

Mnesia 集群使用全网状拓扑结构:即每个节点都会与集群中其它所有的节点建立连接,每个事务都被会复制到集群中的所有节点。如下图所示:

Mnesia 网状拓扑

Mnesia 的问题

正如我们上面所讨论的,Mnesia 数据库有很多非常显著的优点,EMQX 也从中获得了非常大的收益。但其全连接的特性,限制了其集群的水平扩展能力,因为节点之间的链接数量随着节点数量的平方而增长,保持所有节点完全同步的成本越来越高,事务执行的性能也会急剧下降

这意味着 EMQX 的集群功能有以下限制:

  • 水平扩展能力不足。 在 4.x 我们不建议在集群节点过多,因为网状拓扑中的事务复制的开销会越来越大;我们一般建议是使用节点数保持在 3 ~ 7 个,并尽量提供单节点的性能。
  • 节点数增多会增大集群脑裂的可能性。节点数越多、节点间的链接数也会急剧增多,对节点间的网络稳定性的要求更高。当产生脑裂后,节点自愈会导致节点重启并有数据丢失的风险。

尽管如此,EMQX 凭借独特的架构设计和 Erlang/OTP 强大的功能特性,实现了单个集群 1000 万 MQTT 连接的目标。同时,EMQX 能够以集群桥接的方式,通过多个集群承载更大规模的物联网应用。但随着市场的发展,单个物联网应用需要承载越来越多的设备和用户,EMQX 需要具备更强大的扩展性和接入能力,以支持超大规模物联网应用。

5.x 时代:使用 Mria 构建大规模集群

Mria 是 Mnesia 的一个开源扩展,为集群增加了最终的一致性。前文所述的大多数特性仍然适用于它,区别在于数据如何在节点间进行复制。 Mria 从全网状拓扑结构转向网状+星型状拓扑结构。每个节点承担两个角色中的一个:核心节点(Core)复制者节点(Replicant)

Mria 核心-复制节点拓扑

Core 和 Replicant 节点行为

Core 节点的行为与 4.x 中的 Mnesia 节点一致:Core 节点使用全连接的方式组成集群,每个节点都可以发起事务、持有锁等。因此,EMQX 5.0 仍然要求 Core 节点在部署上要尽量的可靠。

Replicant 节点不再直接参与事务的处理。但它们会连接到 Core 节点,并被动地复制来自 Core 节点的数据更新。Replicant 节点不允许执行任何的写操作。而是将其转交给 Core 节点代为执行。另外,由于 Replicant 会复制来自 Core 节点的数据,所以它们有一份完整的本地数据副本,以达到最高的读操作的效率,这样有助于降低 EMQX 路由的时延。

我们可以将这种数据复制模型当做无主复制和主从复制的一种混合。这种集群拓扑结构解决了两个问题:

  • 水平可扩展性(如前文提到,我们已经测试了有 23 个节点的 EMQX 集群)
  • 更容易的集群自动扩展,并无数据丢失的风险。

由于 Replicant 节点不参与写操作,当更多的 Replicant 节点加入集群时,写操作的延迟不会受到影响。这允许创建更大的 EMQX 集群。

另外,Replicant 节点被设计成是无状态的。添加或删除它们不会导致集群数据的丢失、也不会影响其他节点的服务状态,所以 Replicant 节点可以被放在一个自动扩展组中,从而实现更好的 DevOps 实践。

出于性能方面的考虑,不相干数据的复制可以被分成独立的数据流,即多个相关的数据表可以被分配到同一个 RLOG Shard(复制日志分片),顺序地把事务从 Core 节点复制到 Replicant 节点。但不同的 RLOG Shard 之间是异步的。

EMQX 5.0 集群部署实践

集群架构选择

在 EMQX 5.0 中,所以如果不做任何调整的话所有节点都默认为 Core 节点,默认行为和 4.x 版本是一致的。

可以通过设置 emqx.conf 中的 node.db_role 参数或 EMQX_NODE__DB_ROLE 环境变量,把节点上设置为 Replicant 节点。

请注意,集群中至少要有一个核心节点,我们建议以 3 个 Core + N 个 Replicant 的设置作为开始

Core 节点可以接受 MQTT 的业务流量,也可以纯粹作为集群的数据库来使用。我们建议:

  • 在小集群中(3 个节点或更少),没有必要使用 Core + Replicant 复制模式,可以让 Core 节点承担所有的流量,避免增加上手和使用的难度。
  • 在超大的集群中(10 个节点或更多),建议把 MQTT 流量从 Core 节点移走,这样更加稳定性和水平扩展性更好。
  • 在中型集群中,取决于许多因素,需要根据用户实际的场景测试才能知道哪个更优。

异常处理

Core 节点对于 Replicant 节点是无感的,当某一 Core 节点宕机时,Replicant 节点会自动连接到新的 Core 节点,此过程中客户端不会掉线,但可能导致路由更新延迟;当 Replicant 节点宕机时,所有连接到该节点的客户端会被断开,但由于 Replicant 是无状态的,所以不会影响到其他节点的稳定性,此时客户端需要设置重连机制,连接至另一个 Replicant 节点。

硬件配置要求

网络

Core 节点之间的网络延迟建议 10ms 以下,实测高于 100ms 将不可用,请将 Core 节点部署在同一个私有网络下;Replicant 与 Core 节点之间同样建议部署在同一个私有网络下,但网络质量要求可以比 Core 节点间略低。

CPU 与内存

Core 节点需要较大的内存,在不承接连接的情况下,CPU 消耗较低;Replicant 节点硬件配置与 4.x 一致,可按连接和吞吐配置估算其内存要求。

监控和调试

对 Mria 的性能监控可以使用 Prometheus 或使用 EMQX 控制台查看。 Replicant 节点在启动过程中会经历以下状态:

  • bootstrap:当 Replicant 节点启动后,需要从 Core 节点同步最新数据表的过程
  • local_replay:当节点完成 bootstrap 时,它必须重放这个过程中产生的的写事务
  • normal:当缓存的事务被完全执行后,节点即进入到正常运行的状态。后续的写事务被实时地应用到当前节点。大多数情况下,Replicant 节点都会保持在这个状态。

Prometheus 监控

Core 节点

  • emqx_mria_last_intercepted_trans: 自节点启动以来,分片区收到的交易数量。请注意,这个值在不同的核心节点上可能是不同的。
  • emqx_mria_weight: 一个用于负载平衡的值。它的变化取决于核心节点的瞬间负载。
  • emqx_mria_replicants:连接到核心节点的复制器的数量,为给定的分片复制数据。
  • emqx_mria_server_mql: 未处理的交易数量,等待发送至复制者。越少越好。如果这个指标有增长的趋势,需要更多的核心节点。

Replicant 节点

  • emqx_mria_lag:复制体滞后,表示复制体滞后上游核心节点的程度。越少越好。
  • emqx_mria_bootstrap_time:复制体启动过程中花费的时间。这个值在复制体的正常运行过程中不会改变。
  • emqx_mria_bootstrap_num_keys:在引导期间从核心节点复制的数据库记录的数量。这个值在复制体的正常运行中不会改变。
  • emqx_mria_message_queue_len:复制进程的消息队列长度。应该一直保持在0左右。
  • emqx_mria_replayq_len: 复制体的内部重放队列的长度。越少越好。

控制台命令

./bin/emqx eval mria_rlog:status(). 可以获取关于 Mria 数据库运行状态的更多信息。

注:它可以显示一些 shard 为 down 状态,这表明这些分片没有被任何业务应用使用。

结语

全新的底层架构使 EMQX 5.0 具备了更强的水平扩展能力,在构建满足用户业务需求的更大规模集群的同时,可以降低大规模部署下的脑裂风险以及脑裂后的影响,有效减少集群维护开销,为用户提供更加稳定可靠的物联网数据接入服务。

参考资料:

相关实践学习
消息队列RocketMQ版:基础消息收发功能体验
本实验场景介绍消息队列RocketMQ版的基础消息收发功能,涵盖实例创建、Topic、Group资源创建以及消息收发体验等基础功能模块。
消息队列 MNS 入门课程
1、消息队列MNS简介 本节课介绍消息队列的MNS的基础概念 2、消息队列MNS特性 本节课介绍消息队列的MNS的主要特性 3、MNS的最佳实践及场景应用 本节课介绍消息队列的MNS的最佳实践及场景应用案例 4、手把手系列:消息队列MNS实操讲 本节课介绍消息队列的MNS的实际操作演示 5、动手实验:基于MNS,0基础轻松构建 Web Client 本节课带您一起基于MNS,0基础轻松构建 Web Client
目录
相关文章
|
2月前
|
消息中间件 存储 Java
RocketMQ(一):消息中间件缘起,一览整体架构及核心组件
【10月更文挑战第15天】本文介绍了消息中间件的基本概念和特点,重点解析了RocketMQ的整体架构和核心组件。消息中间件如RocketMQ、RabbitMQ、Kafka等,具备异步通信、持久化、削峰填谷、系统解耦等特点,适用于分布式系统。RocketMQ的架构包括NameServer、Broker、Producer、Consumer等组件,通过这些组件实现消息的生产、存储和消费。文章还提供了Spring Boot快速上手RocketMQ的示例代码,帮助读者快速入门。
|
4月前
|
消息中间件 存储 Java
RabbitMQ 在微服务架构中的高级应用
【8月更文第28天】在微服务架构中,服务之间需要通过轻量级的通信机制进行交互。其中一种流行的解决方案是使用消息队列,如 RabbitMQ,来实现异步通信和解耦。本文将探讨如何利用 RabbitMQ 作为服务间通信的核心组件,并构建高效的事件驱动架构。
178 2
|
3月前
|
消息中间件 弹性计算 运维
云消息队列RabbitMQ 版架构优化评测
云消息队列RabbitMQ 版架构优化评测
71 6
|
4月前
|
网络协议 Java 物联网
MQTT(EMQX) - SpringBoot 整合MQTT 连接池 Demo - 附源代码 + 在线客服聊天架构图
MQTT(EMQX) - SpringBoot 整合MQTT 连接池 Demo - 附源代码 + 在线客服聊天架构图
996 3
|
4月前
|
物联网 C# 智能硬件
智能家居新篇章:WPF与物联网的智慧碰撞——通过MQTT协议连接与控制智能设备,打造现代科技生活的完美体验
【8月更文挑战第31天】物联网(IoT)技术的发展使智能家居设备成为现代家庭的一部分。通过物联网,家用电器和传感器可以互联互通,实现远程控制和状态监测等功能。本文将探讨如何在Windows Presentation Foundation(WPF)应用中集成物联网技术,通过具体示例代码展示其实现过程。文章首先介绍了MQTT协议及其在智能家居中的应用,并详细描述了使用Wi-Fi连接方式的原因。随后,通过安装Paho MQTT客户端库并创建MQTT客户端实例,演示了如何编写一个简单的WPF应用程序来控制智能灯泡。
156 0
|
4月前
|
消息中间件 Arthas Java
RocketMQ—一次连接namesvr失败的案例分析
项目组在使用RocketMQ时遇到Consumer连接Name Server失败的问题,异常显示连接特定地址失败。通过Arthas工具逐步分析代码执行路径,定位到创建Channel返回空值导致异常。进一步跟踪发现,问题源于Netty组件在初始化`ByteBufAllocator`时出现错误。分析依赖后确认存在Netty版本冲突。解决方法为排除冲突的Netty包,仅保留兼容版本。
307 0
RocketMQ—一次连接namesvr失败的案例分析
|
4月前
|
消息中间件 Java RocketMQ
微服务架构师的福音:深度解析Spring Cloud RocketMQ,打造高可靠消息驱动系统的不二之选!
【8月更文挑战第29天】Spring Cloud RocketMQ结合了Spring Cloud生态与RocketMQ消息中间件的优势,简化了RocketMQ在微服务中的集成,使开发者能更专注业务逻辑。通过配置依赖和连接信息,可轻松搭建消息生产和消费流程,支持消息过滤、转换及分布式事务等功能,确保微服务间解耦的同时,提升了系统的稳定性和效率。掌握其应用,有助于构建复杂分布式系统。
75 0
|
5月前
|
消息中间件 开发工具 RocketMQ
消息队列 MQ使用问题之一直连接master失败,是什么原因
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
|
5月前
|
消息中间件 Java 物联网
消息队列 MQ操作报错合集之建立连接时发生了超时错误,该如何解决
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。
消息队列 MQ操作报错合集之建立连接时发生了超时错误,该如何解决
|
5月前
|
消息中间件 JavaScript Linux
消息队列 MQ操作报错合集之客户端在启动时遇到了连接错误,是什么原因
消息队列(MQ)是一种用于异步通信和解耦的应用程序间消息传递的服务,广泛应用于分布式系统中。针对不同的MQ产品,如阿里云的RocketMQ、RabbitMQ等,它们在实现上述场景时可能会有不同的特性和优势,比如RocketMQ强调高吞吐量、低延迟和高可用性,适合大规模分布式系统;而RabbitMQ则以其灵活的路由规则和丰富的协议支持受到青睐。下面是一些常见的消息队列MQ产品的使用场景合集,这些场景涵盖了多种行业和业务需求。