📣 极限科技诚招搜索运维工程师(Elasticsearch/Easysearch)- 全职/北京 👉 : 立即申请加入

适用版本: 7.0-8.9

1. 错误异常的基本描述 #

became follower 是 Elasticsearch 集群协调层(Coordination Layer)抛出的异常,完整异常类通常为 CoordinationStateRejectedException: became follower。该异常表示当前节点在尝试以候选者(candidate)或领导者(leader)身份处理加入请求时,因集群状态发生变更而被迫转为追随者(follower)角色,导致正在进行的加入请求被拒绝。

该异常本身不一定是故障,而是集群选举过程中的正常状态转换信号。但在以下场景中需要引起注意:

  • 集群频繁出现此异常,说明选举过程不稳定,可能存在网络抖动或节点资源问题。
  • 节点长时间无法完成选举,导致集群无法选出主节点或无法稳定提供服务。
  • 伴随其他异常(如 master_not_discovered_exceptionnode_left)同时出现,说明集群存在更严重的协调问题。

常见现象 #

  • 节点日志中反复出现 CoordinationStateRejectedException: became follower 日志条目。
  • 集群状态频繁变更,_cat/health_cluster/health 显示集群状态在 red / yellow / green 之间反复切换。
  • _cat/master 显示主节点频繁变更,或长时间无法选出主节点。
  • 数据写入和搜索请求间歇性失败,返回 master_not_discovered_exceptionno 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.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_hostscluster.initial_master_nodes 配置不完整,导致节点无法正确发现彼此。
  • 脑裂场景:网络分区导致集群出现多个候选主节点,分区恢复后角色重新协商。
  • 节点频繁重启:节点反复加入和离开集群,导致选举状态反复变化。
  • 版本不兼容:混合部署不同版本的 Elasticsearch 节点,协调协议存在差异。

3. 如何排查这个异常 #

第一步:确认集群当前状态 #

# 查看集群健康状态
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"

第二步:检查节点日志 #

在出现异常的节点上查找相关日志:

# 搜索协调相关日志
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

第三步:检查网络连通性 #

# 从当前节点测试与其他节点的连通性
curl -X GET "http://<other_node_ip>:9200/"

# 检查节点间传输端口(默认 9300)是否可达
telnet <other_node_ip> 9300

第四步:检查节点资源 #

# 查看 JVM 堆使用情况
curl -X GET "localhost:9200/_nodes/stats/jvm?pretty"

# 查看节点负载
curl -X GET "localhost:9200/_nodes/stats/os?pretty"

4. 如何解决这个错误 #

场景一:偶发异常(无需特殊处理) #

如果异常只是偶尔出现,且集群状态正常、选举能快速完成,通常无需特殊处理。这是分布式系统选举过程中的正常现象。

场景二:频繁出现选举不稳定 #

1. 检查并优化网络配置

确保节点间网络稳定,必要时调整以下配置:

# 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 节点以上的集群,确保有奇数个具备主节点资格的节点:

# elasticsearch.yml
node.roles: [ master, data ]  # 或单独设置 node.master: true
cluster.initial_master_nodes:  # 仅在首次启动集群时需要
  - "node1"
  - "node2"
  - "node3"

4. 重启节点时按顺序操作

  • 先重启追随者节点,最后重启主节点,避免触发不必要的选举。
  • 使用 _cluster/allocation/explain API 确认分片分配状态后再继续操作。

场景三:集群无法选出主节点 #

如果集群长时间无法选出主节点,可尝试:

# 强制将某个节点设为投票配置中的首选主节点(需谨慎操作)
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 可实时查看集群健康状态、节点角色变更历史、选举频率和异常趋势,帮助快速判断是网络问题、资源问题还是配置问题。
  • INFINI Gateway 可在集群不稳定时提供请求缓存和熔断保护,避免客户端因集群选举抖动而大量报错。

6. 小结 #

became follower 异常本身是 Elasticsearch 集群协调机制的一部分,表示节点在选举过程中角色发生了变更。单独的偶发异常通常无需处理;但如果频繁出现,则需要重点排查网络稳定性、节点资源使用和集群配置。通过建立完善的监控和遵循主节点配置最佳实践,可以有效降低此类异常的发生频率,保障集群稳定。

相关错误 #

附:日志上下文 #

以下为源码中触发该异常的相关逻辑片段,便于结合异常调用栈定位问题:

// 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.