--- title: "became follower - 如何解决此 Elasticsearch 异常" date: 2026-03-18 lastmod: 2026-03-18 description: "became follower是Elasticsearch集群协调过程中的常见异常,表示节点在参与选举或加入集群时角色变更为追随者,本文详细解析其成因、排查步骤与解决方案。" tags: ["集群协调", "节点角色", "领导选举", "追随者节点", "CoordinationStateRejectedException"] summary: "适用版本: 7.0-8.9 1. 错误异常的基本描述 # became follower 是 Elasticsearch 集群协调层(Coordination Layer)抛出的异常,完整异常类通常为 CoordinationStateRejectedException: became follower。该异常表示当前节点在尝试以候选者(candidate)或领导者(leader)身份处理加入请求时,因集群状态发生变更而被迫转为追随者(follower)角色,导致正在进行的加入请求被拒绝。 该异常本身不一定是故障,而是集群选举过程中的正常状态转换信号。但在以下场景中需要引起注意: 集群频繁出现此异常,说明选举过程不稳定,可能存在网络抖动或节点资源问题。 节点长时间无法完成选举,导致集群无法选出主节点或无法稳定提供服务。 伴随其他异常(如 master_not_discovered_exception、node_left)同时出现,说明集群存在更严重的协调问题。 常见现象 # 节点日志中反复出现 CoordinationStateRejectedException: became follower 日志条目。 集群状态频繁变更,_cat/health 或 _cluster/health 显示集群状态在 red / yellow / green 之间反复切换。 _cat/master 显示主节点频繁变更,或长时间无法选出主节点。 数据写入和搜索请求间歇性失败,返回 master_not_discovered_exception 或 no master node 错误。 Kibana 或其他客户端频繁断开连接,提示无法连接到 Elasticsearch 集群。 典型报错与异常栈 # CoordinationStateRejectedException: became follower at org.elasticsearch.cluster.coordination.Coordinator.handleJoinRequest(Coordinator.java:XXX) at org.elasticsearch.cluster.coordination.JoinHelper.lambda$new$0(JoinHelper.java:XXX) 或出现在节点加入集群时: [INFO ][o.e.c.c.Coordinator ] [node_name] cluster UUID changed, joining master [master_node] with term [X] [WARN ][o." --- > **适用版本:** 7.0-8.9 ## 1. 错误异常的基本描述 `became follower` 是 Elasticsearch 集群协调层(Coordination Layer)抛出的异常,完整异常类通常为 `CoordinationStateRejectedException: became follower`。该异常表示当前节点在尝试以候选者(candidate)或领导者(leader)身份处理加入请求时,因集群状态发生变更而被迫转为追随者(follower)角色,导致正在进行的加入请求被拒绝。 该异常本身不一定是故障,而是集群选举过程中的正常状态转换信号。但在以下场景中需要引起注意: - 集群频繁出现此异常,说明选举过程不稳定,可能存在网络抖动或节点资源问题。 - 节点长时间无法完成选举,导致集群无法选出主节点或无法稳定提供服务。 - 伴随其他异常(如 `master_not_discovered_exception`、`node_left`)同时出现,说明集群存在更严重的协调问题。 ### 常见现象 - 节点日志中反复出现 `CoordinationStateRejectedException: became follower` 日志条目。 - 集群状态频繁变更,`_cat/health` 或 `_cluster/health` 显示集群状态在 `red` / `yellow` / `green` 之间反复切换。 - `_cat/master` 显示主节点频繁变更,或长时间无法选出主节点。 - 数据写入和搜索请求间歇性失败,返回 `master_not_discovered_exception` 或 `no master node` 错误。 - Kibana 或其他客户端频繁断开连接,提示无法连接到 Elasticsearch 集群。 ### 典型报错与异常栈 ```text CoordinationStateRejectedException: became follower at org.elasticsearch.cluster.coordination.Coordinator.handleJoinRequest(Coordinator.java:XXX) at org.elasticsearch.cluster.coordination.JoinHelper.lambda$new$0(JoinHelper.java:XXX) ``` 或出现在节点加入集群时: ```text [INFO ][o.e.c.c.Coordinator ] [node_name] cluster UUID changed, joining master [master_node] with term [X] [WARN ][o.e.c.c.JoinHelper ] [node_name] failed to join {master_node} CoordinationStateRejectedException[became follower] ``` ## 2. 为什么会发生这个错误 Elasticsearch 7.x 之后引入了基于 Raft 的集群协调实现(由 `Coordinator` 类管理)。节点在集群中的角色通过选举确定: - **Leader(领导者/主节点)**:负责集群状态管理和协调。 - **Follower(追随者)**:接受领导者管理,参与选举投票。 - **Candidate(候选者)**:在选举过程中临时角色。 `became follower` 异常的产生机制如下: 当节点 A 以候选者身份向节点 B 发送加入请求(join request)时,如果节点 B 在此时因收到更高任期(term)的投票或发现其他主节点,而将自身角色切换为 follower,那么节点 B 会拒绝节点 A 的加入请求,并抛出 `CoordinationStateRejectedException: became follower`。 常见原因包括: - **网络不稳定**:节点间通信延迟或丢包,导致心跳超时,触发重新选举。 - **节点资源不足**:CPU 或内存压力过大,导致节点响应心跳超时,被其他节点认为已失效。 - **集群配置不当**:`discovery.seed_hosts` 或 `cluster.initial_master_nodes` 配置不完整,导致节点无法正确发现彼此。 - **脑裂场景**:网络分区导致集群出现多个候选主节点,分区恢复后角色重新协商。 - **节点频繁重启**:节点反复加入和离开集群,导致选举状态反复变化。 - **版本不兼容**:混合部署不同版本的 Elasticsearch 节点,协调协议存在差异。 ## 3. 如何排查这个异常 ### 第一步:确认集群当前状态 ```bash # 查看集群健康状态 curl -X GET "localhost:9200/_cluster/health?pretty" # 查看主节点信息 curl -X GET "localhost:9200/_cat/master?v" # 查看节点列表及角色 curl -X GET "localhost:9200/_cat/nodes?v&h=name,role,master,ip,heap.percent,ram.percent,cpu,load_1m,uptime" ``` ### 第二步:检查节点日志 在出现异常的节点上查找相关日志: ```bash # 搜索协调相关日志 grep -E "CoordinationStateRejectedException|became follower|master|election" /var/log/elasticsearch/elasticsearch.log | tail -100 # 搜索节点加入相关日志 grep -E "joining|join|cluster UUID" /var/log/elasticsearch/elasticsearch.log | tail -50 ``` ### 第三步:检查网络连通性 ```bash # 从当前节点测试与其他节点的连通性 curl -X GET "http://:9200/" # 检查节点间传输端口(默认 9300)是否可达 telnet 9300 ``` ### 第四步:检查节点资源 ```bash # 查看 JVM 堆使用情况 curl -X GET "localhost:9200/_nodes/stats/jvm?pretty" # 查看节点负载 curl -X GET "localhost:9200/_nodes/stats/os?pretty" ``` ## 4. 如何解决这个错误 ### 场景一:偶发异常(无需特殊处理) 如果异常只是偶尔出现,且集群状态正常、选举能快速完成,通常无需特殊处理。这是分布式系统选举过程中的正常现象。 ### 场景二:频繁出现选举不稳定 **1. 检查并优化网络配置** 确保节点间网络稳定,必要时调整以下配置: ```yaml # elasticsearch.yml # 增加心跳超时时间(默认 1s,可适当调大) discovery.request_peers_timeout: 1s discovery.find_peers_interval: 1s # 确保 seed_hosts 配置完整 discovery.seed_hosts: - "node1:9300" - "node2:9300" - "node3:9300" ``` **2. 检查节点资源,降低负载** - 确保节点 JVM 堆大小不超过物理内存的 50%,且不超过 32GB。 - 检查是否有大量垃圾回收(GC)导致节点暂停,必要时优化查询或增加节点。 - 避免在主节点上运行大量数据写入或复杂聚合任务。 **3. 确认主节点配置正确** 对于 3 节点以上的集群,确保有奇数个具备主节点资格的节点: ```yaml # elasticsearch.yml node.roles: [ master, data ] # 或单独设置 node.master: true cluster.initial_master_nodes: # 仅在首次启动集群时需要 - "node1" - "node2" - "node3" ``` **4. 重启节点时按顺序操作** - 先重启追随者节点,最后重启主节点,避免触发不必要的选举。 - 使用 `_cluster/allocation/explain` API 确认分片分配状态后再继续操作。 ### 场景三:集群无法选出主节点 如果集群长时间无法选出主节点,可尝试: ```bash # 强制将某个节点设为投票配置中的首选主节点(需谨慎操作) curl -X POST "localhost:9200/_cluster/voting_config_exclusions?pretty" # 检查投票配置 curl -X GET "localhost:9200/_cluster/state?filter_path=metadata.cluster_coordination&pretty" ``` ## 5. 预防建议与最佳实践 - **保持奇数个主节点候选者**:3 或 5 个具备 `master` 角色的节点,避免偶数导致选举僵局。 - **分离主节点与数据节点**:在规模较大的集群中,使用专用主节点(仅配置 `node.roles: [ master ]`),不参与数据存储和查询,降低资源竞争。 - **监控集群选举频率**:通过 INFINI Console 监控集群状态变更历史,及时发现选举异常。 - **网络隔离**:确保 Elasticsearch 节点间网络专享或优先保障,避免与高流量业务共用网络链路。 - **定期巡检**:定期检查 `_cluster/pending_tasks` API,确认是否有堆积的任务影响集群稳定性。 ### 借助 INFINI 产品提升排障效率 - [INFINI Console](https://docs.infinilabs.com/console/main/) 可实时查看集群健康状态、节点角色变更历史、选举频率和异常趋势,帮助快速判断是网络问题、资源问题还是配置问题。 - [INFINI Gateway](https://docs.infinilabs.com/gateway/main/) 可在集群不稳定时提供请求缓存和熔断保护,避免客户端因集群选举抖动而大量报错。 ## 6. 小结 `became follower` 异常本身是 Elasticsearch 集群协调机制的一部分,表示节点在选举过程中角色发生了变更。单独的偶发异常通常无需处理;但如果频繁出现,则需要重点排查网络稳定性、节点资源使用和集群配置。通过建立完善的监控和遵循主节点配置最佳实践,可以有效降低此类异常的发生频率,保障集群稳定。 ## 附:日志上下文 以下为源码中触发该异常的相关逻辑片段,便于结合异常调用栈定位问题: ```java // Coordinator.java 中处理加入请求的逻辑 if (newMode == Mode.LEADER) { // 处理成为 leader 的逻辑 joiningTerm = ... } else { assert newMode == Mode.FOLLOWER : newMode; // 当节点变为 follower 时,拒绝所有待处理的加入请求 joinRequestAccumulator.values() .forEach(joinCallback -> joinCallback.v2().onFailure( new CoordinationStateRejectedException("became follower"))); } // CandidateJoinAccumulator is only closed when becoming leader or follower; // otherwise it accumulates all joins received regardless of term. ```