设计概述

EMQ X 开源 MQTT 消息服务器在 1.x 版本的基础上,首先分离前端协议(FrontEnd)与后端集成(Backend),其次分离了消息路由平面(Flow Plane)与监控管理平面(Monitor/Control Plane)。EMQ 2.0 消息服务器将在稳定支持 100 万 MQTT 连接的基础上,向可管理可监控坚如磐石的稳定性方向迭代演进:

image

EMQ X 在开源 EMQ X 2.0 版本基础上,大幅改进系统集群设计,采用 Scalable RPC 机制,分离节点间的集群与数据转发通道,以支持更稳定的节点集群与更高性能的消息路由。

EMQ X 企业版在 Backend 端支持 MQTT 消息数据存储 Redis、MySQL、PostgreSQL、MongoDB、Cassandra 多种数据库,支持桥接转发 MQTT 消息到 Kafka、RabbitMQ 企业消息中间件。

100 万连接

多核服务器和现代操作系统内核层面,可以很轻松支持 100 万 TCP 连接,核心问题是应用层面如何处理业务瓶颈。

EMQ 消息服务器在业务和应用层面,解决了承载 100 万连接的各类瓶颈问题。连接测试的操作系统内核、TCP 协议栈、Erlang 虚拟机参数: http://docs.emqtt.cn/zh_CN/latest/tune.html 设计概述 - 图2 (opens new window)

全异步架构

EMQ 消息服务器是基于 Erlang/OTP 平台的全异步的架构:异步 TCP 连接处理、异步主题(Topic)订阅、异步消息发布。只有在资源负载限制部分采用同步设计,比如 TCP 连接创建和 Mnesia 数据库事务执行。

一条 MQTT 消息从发布者(Publisher)到订阅者(Subscriber),在 EMQ X 消息服务器内部异步流过一系列 Erlang 进程 Mailbox:

image

消息持久化

EMQ 1.0 版本不支持服务器内部消息持久化,这是一个架构设计选择。首先,EMQ 解决的核心问题是连接与路由;其次,我们认为内置持久化是个错误设计。

传统内置消息持久化的 MQ 服务器,比如广泛使用的 JMS 服务器 ActiveMQ,几乎每个大版本都在重新设计持久化部分。内置消息持久化在设计上有两个问题:

  1. 如何平衡内存与磁盘使用?消息路由基于内存,消息存储是基于磁盘。
  2. 多服务器分布集群架构下,如何放置 Queue?如何复制 Queue 的消息?

Kafka 在上述问题上,做出了正确的设计:一个完全基于磁盘分布式 commit log 的消息服务器。

EMQ X 企业版本支持消息持久化到 Redis、MySQL、PostgreSQL、MongoDb、Cassandra 等数据库或 Kafka。

设计上分离消息路由与消息存储职责后,数据复制容灾备份甚至应用集成,可以在数据层面灵活实现。

NetSplit 问题

EMQ 1.0 消息服务器集群,基于 Mnesia 数据库设计。NetSplit 发生时,节点间状态是:Erlang 节点间可以连通,互相询问自己是否宕机,对方回答你已经宕机:(

NetSplit 故障发生时,EMQ X 消息服务器的 log/emqx_error.log 日志,会打印 critical 级别日志:

  1. Mnesia inconsistent_database event: running_partitioned_network, emqx@host

EMQ 集群部署在同一 IDC 网络下,NetSplit 发生的几率很低,一旦发生又很难自动处理。所以 EMQ .0 版本设计选择是,集群不自动化处理 NetSplit,需要人工重启部分节点。