3. EMQX集群发现与组建机制
EMQX 集群的组建分为两个阶段:节点发现与集群建立。节点发现解决的是“新节点如何知道集群里还有哪些节点”的问题;集群建立解决的是“知道彼此之后,如何形成一致的 Erlang 分布式集群并同步路由与订阅关系”的问题。EMQX 支持多种发现方式,包括手动发现(manual)、静态节点列表(static)、DNS 发现(dns)、etcd 发现、K8s 发现等。不同方式适用于不同的部署环境,选择错误会导致节点无法加入集群、集群分裂或节点重复加入等问题。
一、集群发现的基本原理
EMQX 基于 Erlang/OTP 的分布式能力构建集群。每个 EMQX 节点启动时会生成一个节点名,格式为 name@host,例如 emqx@10.0.0.11。节点之间通过 Erlang 分布式协议(默认端口 4370,或使用 TLS 时另配端口)建立连接。集群发现机制的核心任务,就是让每个节点在启动时获得一份“候选节点列表”,然后尝试与列表中的节点建立连接,最终形成一个全互联的集群。
EMQX 的发现策略由 cluster.discovery_strategy 配置项决定,可选值包括 manual、static、dns、etcd、k8s 等。无论采用哪种策略,最终都会把发现的节点交给集群管理模块,由它完成节点连接、路由表同步和订阅关系同步。
二、手动发现(manual)
手动发现是最基础的方式。集群启动时不会自动寻找其他节点,需要运维人员通过命令行或 API 手动将节点加入集群。典型操作如下:
# 在节点 emqx@10.0.0.12 上执行,将其加入 emqx@10.0.0.11 所在的集群
emqx_ctl cluster join emqx@10.0.0.11
# 查看集群状态
emqx_ctl cluster status
手动发现适合节点数量少、部署频率低、需要精确控制集群成员关系的场景,例如测试环境或小规模生产环境。它的缺点是每次扩容都需要人工介入,节点重启后不会自动重新加入集群,需要配合自动化脚本使用。
三、静态节点列表(static)
静态节点列表方式通过配置文件预先定义集群中所有节点的地址。每个节点启动时读取这份列表,并尝试与列表中的其他节点建立连接。配置示例如下:
cluster {
discovery_strategy = static
static {
seeds = ["emqx@10.0.0.11", "emqx@10.0.0.12", "emqx@10.0.0.13"]
}
}
静态列表的优点是配置简单、不依赖外部服务,节点重启后能自动重新加入集群。缺点是节点列表固定,扩容时需要修改所有节点的配置并重启,不适合节点数量频繁变化的场景。它适用于节点数量固定、IP 地址稳定的中小规模集群。
四、DNS 发现(dns)
DNS 发现方式利用 DNS 记录动态获取节点列表。EMQX 支持两种 DNS 记录类型:A 记录和 SRV 记录。使用 A 记录时,所有节点共享同一个域名,DNS 返回多个 IP 地址;使用 SRV 记录时,可以指定每个节点的端口和优先级。配置示例如下:
cluster {
discovery_strategy = dns
dns {
name = "emqx.cluster.local"
record_type = a
}
}
DNS 发现的优势在于节点列表由 DNS 动态维护,扩容时只需更新 DNS 记录,无需修改 EMQX 配置。它适合节点数量较多、IP 地址可能变化的场景,例如云环境或容器化部署。需要注意的是,DNS 解析结果有缓存时间(TTL),节点发现存在一定延迟;同时要确保 DNS 服务本身的高可用,避免 DNS 故障导致集群无法组建。
五、etcd 发现
etcd 发现方式利用 etcd 作为服务注册中心。每个 EMQX 节点启动时将自己的节点信息写入 etcd 的指定路径,同时监听该路径下的其他节点信息,从而实现动态发现。配置示例如下:
cluster {
discovery_strategy = etcd
etcd {
server = "http://10.0.0.100:2379"
prefix = "emqx/cluster"
node_ttl = 60s
}
}
etcd 发现适合已经使用 etcd 作为基础设施的云原生环境。它的优点是节点信息动态更新、支持 TTL 自动清理失效节点、集群规模可弹性伸缩。缺点是需要额外维护 etcd 集群,增加了运维复杂度。
六、Kubernetes 发现(k8s)
在 Kubernetes 环境中,EMQX 通常以 StatefulSet 或 Deployment 方式部署。K8s 发现方式通过调用 Kubernetes API Server 查询指定命名空间下符合标签选择器的 Pod 列表,从而获取集群节点信息。配置示例如下:
cluster {
discovery_strategy = k8s
k8s {
apiserver = "https://kubernetes.default.svc:443"
service_name = "emqx-headless"
namespace = "emqx"
address_type = ip
app_name = "emqx"
}
}
K8s 发现是 EMQX 在 Kubernetes 上部署时的推荐方式。它直接利用 K8s 的服务发现能力,无需额外组件,节点扩缩容时自动感知。使用时需要为 EMQX Pod 配置相应的 ServiceAccount 和 RBAC 权限,允许其读取 Pod 列表。配合 Headless Service,每个 Pod 可以获得稳定的 DNS 名称,便于节点间通信。
七、发现方式对比与选型建议
| 发现方式 | 依赖组件 | 动态扩容 | 适用场景 |
|---|---|---|---|
| manual | 无 | 需人工介入 | 测试环境、小规模固定集群 |
| static | 无 | 需改配置重启 | 节点数量固定、IP 稳定的集群 |
| dns | DNS 服务 | 支持,有 TTL 延迟 | 云环境、容器化部署 |
| etcd | etcd 集群 | 支持,实时性高 | 已有 etcd 基础设施的云原生环境 |
| k8s | K8s API Server | 支持,自动感知 | Kubernetes 环境 |
八、集群组建的关键检查点
无论采用哪种发现方式,集群组建成功后都应检查以下内容:
- 节点连通性:确认所有节点之间的 Erlang 分布式端口(默认 4370)互通,防火墙和安全组规则已放行。
- 节点名唯一性:每个节点的
node.name必须唯一,否则会出现节点名冲突导致加入失败。 - Cookie 一致性:所有节点的
node.cookie必须相同,否则 Erlang 节点之间无法建立信任关系。 - 集群状态:执行
emqx_ctl cluster status,确认所有节点均处于running状态,且节点列表完整。 - 路由与订阅同步:在任意节点发布消息,确认其他节点上的订阅者能正常收到,验证路由表已同步。