2023年7月11日发(作者:)
EMQX与RabbitMQ消息服务器MQTT性能对⽐(下)在中,我们采⽤相同的硬件资源分别对 和 进⾏了压⼒测试。结果表明:在「多对⼀」 场景中,EMQ X 和 RabbitMQ 相⽐并没有太⼤差别;⽽在「⼀对多」场景中,RabbitMQ则较 EMQ X 产⽣了较为明显的差距。本期⽂章中我们将对这⼀结果进⾏进⼀步的解析。造成差距的原因主要有三个:节点间通讯的⽅式、消息流架构的⽅式、队列的使⽤。节点间的通讯RabbitMQ - 委托架构RabbitMQ 使⽤了 Erlang 语⾔的分布式连接,即每个节点之间两两互相连接,每个节点⽤⼀个单⼀的链接连接着另⼀个节点。在图中的情况下,三个节点依次连接;当节点之间需要通信时,⼀条消息需要通过这个单⼀链接从⼀个节点发送到另⼀节点。在扇出(fan-out)的例⼦中,正常来讲你需要将消息推送到所有节点的队列上。RabbitMQ 使⽤的优化⽅式则是:你的消息只需要发送⼀次,之后其内置的代理委托框架会将这⼀条消息派送并且发到其他节点的队列上。这个过程中,消息是有序发送的,所以保证了消息在不同队列⾥都是相同的顺序。但是这个⽅案也不是⼗全⼗美的,因为你会将所有的消息只发送⼀次,在分发⼯作都依靠同⼀个委托进程。⽽且 RabbitMQ 选择这个代理进程的策略是根据发布者的哈希算法。所以,当如果你只有⼀个发布者,所有的消息都会被⼀直推送到单个的委托代理进程。EMQ X - Gen_RPC在 EMQ X 中有个精妙的设计:其不仅存在着分布式连接,还存在着 Gen_RPC。分布连接和 Gen_RPC 各司其职,前者⽤于交换 Mnesia 的数据信息,后者则只适⽤于消息的转发。每当你需要从⼀个节点向另⼀个节点发布⼀个消息的时候,EMQ X 不是重新⾃动⽣成新的节点间链接(默认 1 个连接),再通过这些新的连接去处理把⼀个消息从⼀个节点推送到另⼀个节点的⼯作。⽽是依靠针对此场景特地设计的,专有的 Gen_RPC 连接来处理这个消息推送的⼯作。所以在扇出(⼀对多)的例⼦中,这些链接会被完全有效地利⽤。但这种设计在⽹络分区环境中其性能有可能受到影响,RabbitMQ 节点之间只有⼀个分布式连接,所以当连接断开造成脑裂时,愈合修复的⼯作将会更简单。消息流MQTT 插件RabbitMQ 在使⽤ MQTT 插件后会监听使⽤ 发布的消息。得到消息之后,消息被解析,之后再通过 AMQP 协议进⾏转化,最后才会被发送到 RabbitMQ 上。如果要发送⼀条消息,需要经过套接字后进⼊ mqtt_reader,接下来再进⼊下图所⽰的所有过程。然⽽如果要在同⼀条通道⾥同时接收刚刚发送的这条消息,所有上图所⽰的过程则需要反着重新进⾏⼀次,包括 mqtt_reader。其中,mqtt_reader 不仅负责了读,也负责了写。AMQPAMQP 场景则不同,每条消息都被⼀个 reader 读取,⼀个 writer 写⼊。这两条通道读写独⽴,reader 只负责读内容,⽽ writer 只负责写内容,它们各司其职、相互独⽴。⽽唯⼀的通道 channel 则是⼀个主 Erlang 进程,其负责着消息的交换。可见 RabbitMQ 在 MQTT 场景中存在的明显的设计问题会导致性能下降,那么如果引⼊ AMQP 模式的 RabbitMQ 测试⽤例将会如何呢?将 RabbitMQ 调制成使⽤ MQTT 插件的和使⽤单⼀ AMQP 的模式使⽤,再对⽐ EMQ X 在压⼒测试下的情况,可以看出 EMQ X 在所有测试中仍是更胜⼀筹,但总体来说使⽤ AMQP 模式的 RabbitMQ 要⽐⾃⼰原有的成绩更好。多对⼀此场景中 RabbitMQ 与 EMQ X 已经有了接近的性能表现。⼀对多但如果在 fan-out(⼀对多)场景⾥,EMQ X 仍然具有显著优势,但 RabbitMQ(AMQP)的差距已经明显缩⼩。队列以上的测试均使⽤了 QoS 1 的消息。当发送 QoS 1 的消息时,这些消息每次都要作为可持久化的备份保存在硬盘上。所以队列空间的使⽤也尤为重要。RabbitMQRabbitMQ 成熟地使⽤了⼀个默认的队列空间执⾏⽅式(可以被替换成其他队列使⽤)。这个可变队列在消息的持久度和给客户端发送消息的时延⾥做了均衡。但是在最坏的情况下,⼀个消息可能会被存⼊内存。不过这也帮助了 RabbitMQ 在崩溃重启之后可以让服务器再上线,并且所有的客户端还可重连且收到原来持久化的消息。EMQ XEMQ X 对队列的实现⽅式⾮常简单,即在内存中使⽤了优先队列。如果发来的消息⽆法推⼊接收者的队列,则这个消息会被丢掉。在 EMQ X 中,只有⽤⼀些其他持久化的插件才能使消息持久化保存,这些功能在商业版中提供。EMQ X 的设计初衷是将接⼊层独⽴,所以将消息持久化的问题留给了后端完成。这⼀问题在未来具有持久性会话的版本中会解决(persistence session)。节流RabbitMQ - 控流RabbitMQ 采⽤了⼀种⽐较有名的控流机制,它给每⼀个流程了⼀个信⽤值,如下图所⽰。假设说我们的服务端接收到了⼀个消息并由 reader 进⾏了读取后,这条消息被送到channel。这个过程将会消费掉 reader 和 channel 的相应的信⽤值。这样⼀来,就可以通过使两⽅信⽤值保持匹配同步的⽅法实现不超额的发送了。这其实是⼀个不错的解决⽅案。设想我们有许多的⽤户,即有许多的队列,每发送⼀条消息就意味着将要将这条消息分发给许多的队列,这会严重影响 RabbitMQ 实例。然⽽,这⼀套流程会阻⽌ RabbitMQ 再继续读区接收缓冲区的消息——因为发送缓冲区已经快满了!EMQ X - 限流EMQ X 的节流主要是靠限制读取⼀⽅的流量去实现的。⾸先,根据预设,将会⼀次从套接字内读取 200 条消息。当这些消息被完全收到了之后才会逐个将他们处理。⼀旦套接字报告它已经到达了读取⼀⽅的最⼤限额,它将会检查有发布者的数量和已经被阅读的字节数量,并根据这个数值去休眠⼀段时间。接收缓冲区最终会被填满,发布者根据 TCP协议中飞⾏窗⼝的要求也将不会再发布任何内容。总结以上就是这个横向评测的结果和分析。最终的赢家很难断⾔,但是如果就服务器的性能上来讲,EMQ X 肯定是略胜⼀筹的。不过 RabbitMQ 也有它独特的优势。EMQ X 的设计原则EMQ X 在设计上,⾸先分离了前端协议 (FrontEnd) 与后端集成 (Backend),其次分离了消息路由平⾯ (Flow Plane) 与监控管理平⾯ (Monitor/Control Plane):1. EMQ X 核⼼解决的问题:处理海量的并发 MQTT 连接与路由消息。2. 充分利⽤ Erlang/OTP 平台软实时、低延时、⾼并发、分布容错的优势。3. 连接 (Connection)、会话 (Session)、路由 (Router)、集群 (Cluster) 分层。4. 消息路由平⾯ (Flow Plane) 与控制管理平⾯ (Control Plane) 分离。5. ⽀持后端数据库或 NoSQL 实现数据持久化、容灾备份与应⽤集成。EMQ X 的系统分层1. 连接层 (Connection Layer):负责 TCP 连接处理、 MQTT 协议编解码。2. 会话层 (Session Layer):处理 MQTT 协议发布订阅消息交互流程。3. 路由层 (Route Layer):节点内路由派发 MQTT 消息。4. 分布层 (Distributed Layer):分布节点间路由 MQTT 消息。5. 认证与访问控制 (ACL):连接层⽀持可扩展的认证与访问控制模块。6. 钩⼦ (Hooks) 与插件 (Plugins):系统每层提供可扩展的钩⼦,⽀持插件⽅式扩展服务器。⽽ RabbitMQ 则更类似于 Kafka 的消息队列缓存设计。建议在 IoT 项⽬中将两者结合使⽤。版权声明: 本⽂为 EMQ 原创,转载请注明出处。原⽂链接:技术⽀持:如对本⽂或 EMQ 相关产品有疑问,可访问 EMQ 问答社区 提问,我们将会及时回复⽀持。更多技术⼲货,欢迎关注我们公众号【EMQ 中⽂社区】。
发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1689029053a197407.html
评论列表(0条)