Skip to content

Consul从零到生产级:Agent、Gossip、Raft、Catalog、Health、DNS与Service Mesh ​

Consul 是面向服务网络的分布式控制面,核心能力包括服务注册发现、健康检查、KV、Session、DNS/HTTP查询、多数据中心和Service Mesh。它最容易被讲错的地方是把Gossip和Raft混成“Consul一致性算法”:Gossip负责成员关系和故障信息扩散,Raft负责Server集群内一致状态的排序与提交,两者解决的是不同问题。

本页从Agent、Catalog和Health对象开始,沿Provider注册、健康检查、Consumer DNS/HTTP查询、Blocking Query、Session锁、Connect mTLS、跨数据中心和生产恢复建立完整链路。

一、学习目标 ​

  1. 区分Client Agent、Server Agent、LAN Gossip、WAN Gossip与Raft。
  2. 解释服务从本地Agent注册到Catalog可查询的完整过程。
  3. 区分Catalog存在、Health Passing和业务真正可用。
  4. 使用DNS、HTTP Health API和Blocking Query发现服务。
  5. 理解KV ModifyIndex、CAS、Transaction、Session和Lock Delay。
  6. 解释Session失效后为什么仍需要Fencing Token。
  7. 解释Consul Connect如何通过代理、证书和Intentions实现Service Mesh。
  8. 设计多数据中心发现、ACL/TLS和生产Runbook。

二、Consul适合什么场景 ​

场景Consul能力
VM/裸机服务发现Agent注册、Catalog、DNS/HTTP
健康实例过滤HTTP/TCP/gRPC/TTL等Health Check
多数据中心服务目录每DC独立Server集群、WAN Federation或Peering
少量控制面配置KV、Blocking Query、Watch
Leader选举和低频锁Session + KV acquire/release
Service MeshConnect、sidecar/Envoy、mTLS、Intentions

不适合:

  • 高频订单和业务流水。
  • 大文件和日志。
  • 把Health Passing当作所有业务依赖都健康。
  • 用Session锁替代数据库唯一约束和幂等。
  • 认为跨数据中心存在一个全球Raft集群。

三、Agent架构 ​

mermaid
flowchart TD
    A["业务服务"] --> B["本机或同节点Consul Client Agent"]
    B --> C["LAN Gossip成员池"]
    B --> D["把注册、查询和RPC转发给Consul Server"]
    D --> E["Server Leader和Raft集群"]
    E --> F["Catalog、KV、ACL等一致状态"]
    G["其他数据中心Server"] --> H["WAN Federation或Peering"]

Client Agent ​

  • 通常部署在每个工作节点。
  • 接收本机服务注册和检查配置。
  • 执行本地Health Check。
  • 参与LAN Gossip。
  • 把需要一致状态的RPC转发给Server。
  • 可提供本地DNS和HTTP入口。

Server Agent ​

  • 参与Raft选举和一致状态复制。
  • 保存Catalog、KV、ACL等状态。
  • 也参与LAN Gossip。
  • 多数据中心传统WAN Federation中,Server还参与WAN Gossip。

业务应用可以直接访问Server API,但典型生产拓扑通过本地Client Agent降低Server连接压力并提供节点级能力。

四、Gossip和Raft严格区分 ​

机制参与者保存/传播内容一致性目标
LAN Gossip同数据中心Client与Server Agent成员、地址、故障怀疑、事件快速最终传播
WAN Gossip传统多DC模式中的Server Agent数据中心Server成员和跨DC路由信息最终传播
Raft单数据中心Server投票成员Catalog、KV、ACL、Session等一致状态多数派安全提交

Gossip成员列表不是Catalog的强一致事实源。节点被Gossip标记suspect/dead会影响Catalog和检查处理,但Catalog写入仍由Server Raft提交。

五、LAN Gossip、SWIM思想与Serf ​

Consul使用Serf构建成员通信,底层采用SWIM类故障检测和Gossip传播思想:

mermaid
flowchart TD
    A["Agent周期选择成员Ping"] --> B{"直接ACK是否成功"}
    B -- "是" --> C["成员保持Alive"]
    B -- "否" --> D["请求其他成员间接Ping"]
    D --> E{"间接ACK是否成功"}
    E -- "是" --> C
    E -- "否" --> F["进入Suspect并传播怀疑"]
    F --> G{"在怀疑窗口内是否反驳"}
    G -- "是" --> C
    G -- "否" --> H["判定Failed/Dead并继续传播"]

为什么先Suspect而不是立刻Dead:

  • UDP丢包不等于进程死亡。
  • 单节点网络视角可能错误。
  • 间接探测降低单路径故障误判。
  • 怀疑窗口允许活节点传播更高incarnation信息反驳。

Gossip不是每次状态变化都由中心Server广播,成员相互交换摘要并最终扩散,具有短暂不一致窗口。

六、Server Raft集群 ​

单个数据中心通常部署3或5个Consul Server:

text
3 Server → Quorum 2 → 容忍1个投票Server故障
5 Server → Quorum 3 → 容忍2个投票Server故障

一致写流程:

mermaid
flowchart TD
    A["Client Agent发送注册/KV/Session请求"] --> B["Server Leader追加Raft日志"]
    B --> C["复制给Follower Server"]
    C --> D{"获得多数派确认"}
    D -- "否" --> E["无法提交,等待或重新选举"]
    D -- "是" --> F["提交并Apply到状态机"]
    F --> G["Catalog/KV/Session状态更新"]

Server失去Quorum后,LAN Gossip仍可能看到成员,但Catalog一致写、Session等依赖Raft的能力会受阻。不能用 consul members 全Alive证明Raft健康。

七、查询一致性模式 ​

Consul HTTP查询常见一致性选项包括默认、consistent和stale,精确实现和默认优化随版本:

  • 默认模式通常由Leader/Leader Lease等机制提供较强一致视图,在极端领导权窗口中的语义应按版本文档理解。
  • consistent要求Leader确认仍具有Quorum,提供最强线性一致读取,但延迟和可用性成本更高。
  • stale允许任意Server从本地状态读取,不要求Leader,可在无Leader时继续返回但可能陈旧。

不要把DNS查询、Agent缓存和HTTP consistent视为完全相同的读路径。所有权裁决和锁状态查询应使用有明确强一致保证的API;服务发现可以按可用性目标接受短暂陈旧。

八、Node、Service、Service Instance与Check ​

对象例子说明
Nodevm-10-0-0-11运行Agent的节点身份
Servicestock-api逻辑服务名
Service Instancestock-api-a某节点上的具体实例ID、地址、端口、Tags、Meta
Checkservice:stock-api-a节点或服务健康检查

同一Node可注册多个Service Instance;同一Service可跨多个Node。Service ID在Agent/Node作用域中需要稳定唯一,不能只用服务名导致同节点多个实例互相覆盖。

九、Provider注册完整流程 ​

mermaid
flowchart TD
    A["Provider启动并通过readiness"] --> B["向本地Agent注册Service"]
    B --> C["Agent保存本地服务和Check定义"]
    C --> D["Agent把注册同步给Server"]
    D --> E["Leader经Raft提交Catalog变更"]
    E --> F["Catalog出现Service Instance"]
    F --> G["Agent开始执行Health Check"]
    G --> H["Health状态经Server更新"]
    H --> I["DNS/HTTP健康查询可返回实例"]

注册成功和健康Passing是两个状态:实例先进入Catalog,检查可能仍initializing、warning或critical。Consumer应查询健康接口或使用只返回健康实例的DNS语义,而不是直接读取Catalog所有注册。

9.1 Agent HTTP注册示例 ​

json
{
  "ID": "stock-api-a",
  "Name": "stock-api",
  "Address": "10.0.0.11",
  "Port": 20880,
  "Tags": ["v2", "zone-a"],
  "Meta": {
    "protocol": "dubbo",
    "build": "20260718.1"
  },
  "Check": {
    "HTTP": "http://10.0.0.11:8080/actuator/health/readiness",
    "Interval": "10s",
    "Timeout": "2s",
    "DeregisterCriticalServiceAfter": "10m"
  }
}

注册命令:

bash
curl --request PUT \
  --header "X-Consul-Token: ${CONSUL_HTTP_TOKEN}" \
  --data @stock-service.json \
  http://127.0.0.1:8500/v1/agent/service/register

Token不应写入仓库或Shell历史;生产使用HTTPS和Secret注入。

十、Health Check类型和边界 ​

类型检查什么风险
HTTP状态码、可选Body/TLSEndpoint可能依赖过多下游,抖动扩散
TCP端口能否建立连接端口开不等于业务可用
gRPCgRPC健康协议服务必须实现标准Health
TTL应用定期主动Pass应用卡死或上报线程异常会Critical
Script执行本地脚本安全、资源和命令注入风险
Docker等集成容器状态/命令,依版本Agent权限过大风险

状态通常包括Passing、Warning、Critical。健康查询是否包含Warning要按API和业务策略明确,不能只写“健康”而不定义。

10.1 TTL Check不是Session Lease ​

TTL Check要求应用在TTL内调用Pass/Warning/Fail更新检查状态;超时后Check进入Critical。它主要影响健康筛选,不自动删除任意KV,也不是Consul Session TTL。

应用进程正常但上报线程阻塞,会误Critical;上报线程正常但业务线程池死锁,也可能继续Passing。最佳实践是让检查覆盖必要的业务readiness,但避免把所有非核心下游都绑定成硬失败。

10.2 Critical后自动注销 ​

DeregisterCriticalServiceAfter可在持续Critical后从Catalog删除服务,最小值和实际清理节奏依版本。它不是精确定时器;设置过短会因网络/依赖抖动频繁注销,恢复后还需Agent重新注册。

十一、Catalog查询与Health查询区别 ​

API返回内容是否自动过滤健康
Catalog Service注册的服务实例通常不代表健康
Health Service实例及聚合Check可用passing=true只取Passing
Agent Services本Agent本地注册不是整个数据中心全局目录

生产Consumer通常使用Health API或Consul DNS,而不是Catalog API直接负载均衡。

十二、DNS服务发现 ​

Consul Agent提供DNS接口,典型名称:

text
stock-api.service.consul
stock-api.service.dc1.consul

常见记录:

  • A/AAAA返回实例地址。
  • SRV返回主机、端口和优先级/权重信息。
  • Tag/Prepared Query可提供更复杂选择,语法依版本。
bash
dig @127.0.0.1 -p 8600 stock-api.service.consul A
dig @127.0.0.1 -p 8600 stock-api.service.consul SRV

12.1 DNS缓存边界 ​

DNS结果受:

  • Consul Agent缓存。
  • 递归DNS缓存。
  • 操作系统缓存。
  • JVM DNS缓存。
  • 应用连接池。

实例下线后,旧地址可能在各层TTL内继续存在。Java项目应检查JVM DNS缓存策略和HTTP/RPC客户端是否在长连接池中继续复用旧连接,不能认为Consul Health变Critical后所有请求立刻停止。

十三、HTTP Health API发现 ​

bash
curl \
  --header "X-Consul-Token: ${CONSUL_HTTP_TOKEN}" \
  'https://consul.service.local:8501/v1/health/service/stock-api?passing=true'

响应同时包含Node、Service和Checks。客户端应:

  1. 校验HTTP和JSON。
  2. 只接受目标DC、Tag、Meta和协议兼容实例。
  3. 构建完整新快照后原子替换。
  4. 复用未变化连接,销毁移除实例。
  5. 控制空结果策略,避免瞬时错误清空目录。

十四、Blocking Query原理 ​

轮询每秒请求会让Server承受大量无变化查询。Consul Blocking Query使用响应索引:

  1. 首次请求返回数据和 X-Consul-Index=R。
  2. 下一次请求带 index=R&wait=5m。
  3. 若相关状态未变化,Server挂起请求直到变化或wait超时。
  4. 变化后返回新数据和新Index。
mermaid
flowchart TD
    A["Client首次查询得到Index R"] --> B["请求index=R并设置wait"]
    B --> C{"相关状态是否变化"}
    C -- "否" --> D["Server挂起到超时"]
    D --> B
    C -- "是" --> E["返回新快照和Index R2"]
    E --> F["Client原子替换本地目录"]
    F --> B

14.1 Blocking Query恢复边界 ​

  • wait超时返回不一定表示数据变化,比较Index和内容。
  • Index可能因集群恢复/实现语义变化而回退或异常,客户端应在Index不前进或变小的情况下重建全量状态,而不是永久等待。
  • HTTP连接中断后使用最后已应用Index重新查询。
  • Server会为wait加入随机抖动,避免大量查询同一时刻超时;客户端仍应退避。
  • 不要在一个请求上设置无限wait并假定网络永不断开。

较新API可能使用内容Hash等Blocking机制,具体Header和参数按目标Endpoint文档确认。

14.2 curl实验 ​

bash
curl --include \
  'http://127.0.0.1:8500/v1/health/service/stock-api?passing=true'

curl --include \
  'http://127.0.0.1:8500/v1/health/service/stock-api?passing=true&index=123&wait=5m'

生产需要ACL Token和TLS,Index 123应来自第一次响应,不能写死。

十五、Consumer本地快照 ​

无论DNS还是HTTP,Consumer最终都需要线程安全地址快照:

text
Consul返回完整健康实例集
    ↓
按Tag/Zone/Protocol过滤
    ↓
复用未变化客户端连接
    ↓
创建新增Invoker/Client
    ↓
原子发布不可变List
    ↓
延迟销毁已移除连接

这和Dubbo RegistryDirectory思想相同。Consul只提供发现控制面,不转发普通业务RPC,除非使用Connect代理数据面。

十六、Consul KV和Index ​

KV响应常包含:

  • Key。
  • Base64编码Value。
  • CreateIndex。
  • ModifyIndex。
  • LockIndex。
  • Session。
  • Flags。

CreateIndex表示首次创建的Raft索引位置,ModifyIndex表示最近修改位置。它们不是时间戳。

16.1 CAS更新 ​

text
cas=0        → 仅Key不存在时创建
cas=N        → 仅ModifyIndex等于N时更新
bash
curl --request PUT \
  --data 'timeout=1500' \
  'http://127.0.0.1:8500/v1/kv/config/order?cas=123'

返回false表示比较失败,应重新GET并合并,不得改成无条件PUT覆盖。

16.2 KV Transaction ​

Consul Transaction API可原子执行多个KV/Node/Service/Check相关操作,支持检查索引、设置、删除等动词;单Txn操作数有实现上限,常见版本为有限数量。它只覆盖Consul一致状态,不能原子包含MySQL和MQ。

十七、Session是什么 ​

Consul Session把Node、Health Check、TTL和KV锁所有权组合起来。Session可配置:

  • 关联Node。
  • 关联Checks。
  • TTL和续约。
  • Behavior=release或delete。
  • Lock Delay。

Session失效原因可能包括:

  • TTL未续约。
  • 关联Node/Check进入失效条件。
  • 主动Destroy。
  • Catalog中的Node被移除。

17.1 release与delete ​

  • release:释放KV的Session绑定,Key和值保留。
  • delete:Session失效时删除持有的Key。

选择取决于锁记录是否需要保留诊断信息。两者都不保证外部业务事务回滚。

17.2 Lock Delay ​

Session失效后,Consul可在一段Lock Delay内阻止同Key立即被新Session取得,用于减少旧Leader暂停/网络延迟带来的快速双执行窗口。它是缓冲,不是Fencing:Delay结束后旧客户端仍可能恢复并写外部资源。

十八、KV锁Acquire/Release ​

概念流程:

mermaid
flowchart TD
    A["创建Session"] --> B["PUT key?acquire=sessionId"]
    B --> C{"KV当前是否可取得"}
    C -- "否" --> D["返回false并Blocking Query等待"]
    C -- "是" --> E["KV记录Session并增加LockIndex"]
    E --> F["执行业务并携带Fencing Token"]
    F --> G["PUT key?release=sessionId"]
    G --> H["释放成功或Session失效自动处理"]

Acquire不是普通CAS的替代:必须校验返回布尔值。Release只有持有Session才能成功;ConnectionLoss时结果可能UNKNOWN,应查询KV的Session和LockIndex。

十九、LockIndex与Fencing Token ​

KV锁被新Session成功Acquire时,LockIndex用于表达所有权授予次数,可作为构造Fencing Token的候选。外部资源必须原子记录并拒绝旧Token:

sql
UPDATE scheduler_owner
SET owner = :owner,
    fencing_token = :token
WHERE job_name = :job
  AND fencing_token < :token;

边界:

  • Token必须来自同一锁Key的单调所有权序列。
  • Key删除重建、集群恢复和跨DC不能未经分析直接比较。
  • Session失效后客户端必须停止。
  • 相同Token重试仍需业务幂等。

二十、Prepared Query ​

Prepared Query可以预定义服务查询、Failover、Near排序等策略,让DNS/HTTP通过查询名执行较复杂发现。功能细节、跨DC行为和部分高级能力具有版本/版本类型边界。

使用原则:

  • 查询定义要版本化和审计。
  • Failover不能掩盖数据协议不兼容。
  • 跨DC回退要评估延迟、数据主权和下游容量。
  • 客户端仍需超时、熔断和幂等。

二十一、多数据中心原理 ​

每个数据中心有独立Server Raft集群和Catalog,不是全球所有Server组成一个Raft Quorum:

mermaid
flowchart TD
    A["DC1 Server Raft Cluster"] --> B["DC1 Catalog"]
    C["DC2 Server Raft Cluster"] --> D["DC2 Catalog"]
    A --> E["WAN Federation或Consul Peering"]
    C --> E
    F["DC1 Client查询DC2服务"] --> A
    A --> E
    E --> C

传统WAN Federation使用Server WAN Gossip和请求转发;较新版本提供Cluster Peering等连接方式,拓扑、安全和传递性不同,应按版本选择。

网络分区时DC1本地Raft可继续服务本地Catalog,但跨DC查询/转发可能失败或使用陈旧信息。跨DC服务调用还受WAN延迟和数据面网络影响。

二十二、Consul Connect Service Mesh ​

Connect把身份和授权从IP提升到服务身份:

mermaid
flowchart TD
    A["服务A"] --> B["本地Sidecar Proxy"]
    B --> C["mTLS连接"]
    C --> D["服务B Sidecar Proxy"]
    D --> E["服务B"]
    F["Consul控制面"] --> G["下发证书、发现、Intentions和代理配置"]
    G --> B
    G --> D

核心组件:

  • Connect CA签发服务证书。
  • 服务注册声明sidecar proxy。
  • Envoy等代理建立mTLS。
  • Intentions决定服务身份之间是否允许通信。
  • Consul通过xDS等机制下发动态代理配置,具体协议/版本演进。

22.1 Service Mesh没有消除业务问题 ​

  • mTLS验证服务身份,不验证订单权限。
  • 代理重试可能重复写,接口仍需幂等。
  • 代理超时和应用超时要共享总预算。
  • 两侧代理增加延迟、CPU和排障层次。
  • 控制面短暂不可用时代理可能继续旧配置,配置年龄要监控。
  • Intentions允许不代表Provider健康。

二十三、安全体系 ​

层机制目的
GossipGossip Encryption Key防止未授权成员读取/注入Gossip流量
RPC/HTTPTLS加密并认证Agent/Server/API
ACLToken和Policy限制Service、Node、KV、Session等操作
MeshConnect CA、mTLS、Intentions服务身份和服务间授权

Gossip加密不等于HTTP/RPC TLS;ACL Token也不等于传输加密。生产应启用ACL默认拒绝、最小Policy、Token轮换和审计,避免使用全局Management Token给业务应用。

二十四、常见端口 ​

默认端口作用
8300Server RPC
8301 TCP/UDPLAN Serf Gossip
8302 TCP/UDPWAN Serf Gossip
8500HTTP API
8501HTTPS API,需配置
8502gRPC/xDS等,版本相关
8600 TCP/UDPDNS

端口可配置,云防火墙和NetworkPolicy必须区分Client、Server、LAN/WAN和管理访问,不能全部暴露公网。

二十五、生产部署 ​

  • 每DC部署3或5个Server并分散故障域。
  • bootstrap_expect只用于初始引导预期Server数,形成集群后不要把bootstrap模式当日常修复工具。
  • Server使用持久低延迟磁盘。
  • Client Agent靠近业务服务。
  • 配置retry_join或云自动发现时限制身份和范围。
  • 滚动升级一次一个Server,等Raft健康再继续。
  • 使用Autopilot健康检查和清理能力时理解版本与配置,不自动删除未经确认的Server。

二十六、JDK 8 Demo:Catalog Index与Blocking Query ​

下面的无依赖Demo模拟首次快照、Index变化和Blocking Query等待。

java
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

public class ConsulBlockingQueryDemo {

    static final class Snapshot {
        final long index;
        final List<String> healthyInstances;

        Snapshot(long index, List<String> healthyInstances) {
            this.index = index;
            this.healthyInstances = healthyInstances;
        }
    }

    static final class Catalog {
        private long index;
        private final Map<String, Boolean> passing = new LinkedHashMap<String, Boolean>();

        synchronized void update(String instance, boolean isPassing) {
            passing.put(instance, isPassing);
            index++;
            notifyAll();
        }

        synchronized Snapshot blockingQuery(long waitIndex, long waitMillis)
                throws InterruptedException {
            long deadlineNanos = System.nanoTime()
                    + TimeUnit.MILLISECONDS.toNanos(waitMillis);
            while (index == waitIndex) {
                long remainingNanos = deadlineNanos - System.nanoTime();
                if (remainingNanos <= 0L) break;
                long remainingMillis = TimeUnit.NANOSECONDS.toMillis(remainingNanos);
                wait(Math.max(1L, remainingMillis));
            }
            List<String> result = new ArrayList<String>();
            for (Map.Entry<String, Boolean> entry : passing.entrySet()) {
                if (Boolean.TRUE.equals(entry.getValue())) result.add(entry.getKey());
            }
            return new Snapshot(index, Collections.unmodifiableList(result));
        }
    }

    public static void main(String[] args) throws Exception {
        final Catalog catalog = new Catalog();
        catalog.update("10.0.0.11:20880", true);
        Snapshot first = catalog.blockingQuery(0L, 1L);
        System.out.println("firstIndex=" + first.index + ", instances=" + first.healthyInstances);

        Thread updater = new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(100L);
                    catalog.update("10.0.0.12:20880", true);
                } catch (InterruptedException ex) {
                    Thread.currentThread().interrupt();
                }
            }
        });
        updater.start();

        Snapshot changed = catalog.blockingQuery(first.index, 1000L);
        updater.join();
        System.out.println("changedIndex=" + changed.index + ", instances=" + changed.healthyInstances);
    }
}

预期输出:

text
firstIndex=1, instances=[10.0.0.11:20880]
changedIndex=2, instances=[10.0.0.11:20880, 10.0.0.12:20880]

真实Consul Index来自Raft/Catalog查询响应,不能用客户端本地计数替代;Demo只解释等待和快照发布模型。

二十七、运维命令与证据 ​

bash
consul members
consul members -wan
consul operator raft list-peers
consul operator autopilot health
consul catalog services
consul snapshot save consul.snap

API证据:

bash
curl -H "X-Consul-Token: ${CONSUL_HTTP_TOKEN}" \
  https://127.0.0.1:8501/v1/status/leader

curl -H "X-Consul-Token: ${CONSUL_HTTP_TOKEN}" \
  https://127.0.0.1:8501/v1/operator/autopilot/health

命令名称、ACL要求和输出字段随版本变化,生产先用-help和目标版本文档确认。

二十八、关键监控指标 ​

Gossip ​

  • LAN/WAN成员Alive/Suspect/Failed。
  • Gossip消息、probe失败、队列。
  • 成员频繁Flap。

Raft和Server ​

  • 是否存在Leader、Leader变化。
  • Raft commit/apply延迟、last contact。
  • Server RPC和请求队列。
  • Autopilot健康。
  • Snapshot、磁盘和GC。

Catalog与Health ​

  • Service/Node/Check数量。
  • Passing/Warning/Critical分布。
  • 注册/注销速率。
  • Blocking Query数量和等待时间。
  • DNS/HTTP查询延迟与空结果率。
  • Session数量、失效率和锁等待时间。

Mesh ​

  • 证书过期时间和轮换失败。
  • xDS配置版本/年龄。
  • mTLS握手失败。
  • Intention拒绝和代理上游健康。

二十九、Runbook:Server无Leader ​

  1. consul members只证明LAN成员视图,继续检查Raft peers。
  2. 确认Server可达数达到Quorum。
  3. 检查8300 RPC和LAN网络。
  4. 检查TLS证书、ACL不是Raft Peer本身的唯一问题,但RPC TLS会影响通信。
  5. 检查Server磁盘、fsync、CPU和GC。
  6. 检查是否同时重启/移除多数Server。
  7. 不要对多个Server同时启用bootstrap重新建集群。

三十、Runbook:成员频繁Suspect/Failed ​

  1. 检查8301 TCP/UDP双向连通。
  2. 检查丢包、MTU、NAT和安全组。
  3. 检查Agent CPU、GC和宿主机暂停。
  4. 对比多节点视角,区分局部故障和真实离线。
  5. 检查Gossip Encryption Key是否一致。
  6. 不要仅通过放大怀疑超时长期掩盖网络问题。

三十一、Runbook:服务已注册但Consumer发现不到 ​

  1. 在Provider本地Agent查Service是否注册。
  2. 查Catalog是否已经经Raft提交。
  3. 查聚合Health Check是Passing、Warning还是Critical。
  4. 检查passing=true、Tag、Meta、DC和Prepared Query条件。
  5. DNS场景查8600、递归DNS/JVM缓存和TTL。
  6. HTTP场景查Blocking Index、ACL Token和本地快照。
  7. 最后验证Consumer到Provider业务网络。

三十二、Runbook:地址下线后仍被调用 ​

  • Check变Critical到Catalog/查询传播有延迟。
  • DNS和JVM缓存仍保留旧地址。
  • Consumer连接池复用旧长连接。
  • Consumer使用stale读或本地快照未刷新。
  • Provider被强杀但检查间隔尚未到。
  • DeregisterCriticalServiceAfter尚未清理。

用被动连接失败、超时和熔断缩短影响,不要只依赖注册中心推送。

三十三、Runbook:Session锁失效或双Leader ​

  1. 查询KV Session、LockIndex和Session详情。
  2. 检查关联Node/Check为何失效。
  3. 检查TTL Renew、网络和Agent暂停。
  4. 检查Lock Delay和新Session获取时间。
  5. 确认旧任务在Session失效后停止。
  6. 查看外部资源接受的最大Fencing Token。
  7. 对账重复任务,不要只删除KV后重新抢锁。

三十四、Runbook:Blocking Query不更新或请求暴涨 ​

  • 确认上次Index来自正确Endpoint和Query。
  • Index回退/为0时执行全量重建。
  • wait超时后不要立即无抖动重连。
  • 检查代理/LB的HTTP idle timeout是否小于wait。
  • 检查大量Consumer是否使用短wait造成轮询。
  • 比较Catalog Index变化与本地applied Index。

三十五、Runbook:跨DC发现失败 ​

  1. 本地DC Catalog和Raft是否健康。
  2. WAN members或Peering状态。
  3. Server间8302/相关Peering端口、TLS和ACL。
  4. 目标DC服务和Health。
  5. Prepared Query Failover规则。
  6. 跨DC数据面路由、延迟和防火墙。

三十六、Snapshot备份与恢复 ​

Consul Server Snapshot包含Raft一致状态,应使用官方snapshot命令/API并保留:

  • Consul精确版本。
  • Server配置和数据中心名。
  • Gossip Encryption Key。
  • TLS CA/证书。
  • ACL bootstrap与恢复材料。
  • Connect CA相关密钥保护。

Restore可能覆盖Catalog、KV、ACL和Session相关状态,临时服务/Agent会在恢复后重新同步。必须在隔离环境演练,防止旧集群和恢复集群同时对外提供冲突控制面。

三十七、Consul、Nacos、Eureka、etcd和ZooKeeper对比 ​

组件核心能力健康模型多DC/云原生特点
ConsulAgent、Catalog、Health、DNS、KV、Mesh主动Check丰富多DC、Connect成熟
Nacos服务发现和配置临时/持久实例、客户端/Server检查依版本Spring Cloud Alibaba集成
Eureka应用级注册发现Client续约、Server剔除、自我保护Netflix/Spring Cloud存量
etcd强一致MVCC KV、Watch、Lease、TxnLease为主,健康需上层实现Kubernetes控制面
ZooKeeperznode、Session、Watch、RecipeSession临时节点传统Dubbo、协调Recipe

不能只按“CP/AP”一列选型。要比较服务模型、检查方式、客户端生态、配置、跨DC、运维和数据面治理。

三十八、常见错误及后果 ​

错误后果
Gossip就是Consul一致性协议无法解释Catalog提交和Quorum故障
members全Alive代表Raft健康可能仍无Leader、无法写
Catalog有实例就可调用Critical实例进入流量
TTL Check等于Session TTL错误理解删除和锁所有权
DNS更新后JVM立即丢旧地址缓存和长连接继续调用旧实例
Blocking Query每次短wait无退避Server请求风暴
Session锁不需要Fencing暂停旧Leader覆盖新任务
Gossip加密等于API TLSHTTP/RPC仍可能明文
所有DC组成一个Raft集群错误设计Quorum和故障域
Mesh重试能提高写可靠性重复副作用和故障放大

三十九、面试标准回答 ​

Consul通常由每节点Client Agent和每数据中心3/5个Server组成。LAN Gossip使用Serf/SWIM类机制传播成员和故障怀疑,Server Raft则一致提交Catalog、KV、ACL和Session,两者不能混为一个协议。Provider向本地Agent注册Service和Check,Agent同步到Server Catalog;Consumer通过DNS或Health HTTP查询Passing实例。HTTP Blocking Query使用X-Consul-Index和wait避免高频轮询,客户端收到完整快照后原子替换。TTL Check只更新健康状态,Session则可关联Node/Checks/TTL并用于KV acquire锁;Session失效和Lock Delay仍不能阻止暂停旧客户端写外部资源,因此必须用LockIndex等单调Token做Fencing。多数据中心各自有独立Raft集群,通过WAN Federation或Peering互联;Connect再通过sidecar、mTLS、证书和Intentions提供Service Mesh。

四十、关联知识点 ​

本章小结 ​

Consul的完整链路是“Gossip维护成员视图 → Agent注册和执行Health Check → Server Raft提交Catalog → DNS/HTTP/Blocking Query发现健康实例 → Session/KV提供协调 → Connect下发mTLS数据面配置”。真正掌握它必须同时理解Gossip短暂不一致、Raft Quorum、DNS缓存、检查误判、Session失效和Mesh代理重试边界。