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查询常见一致性选项包括默认、consistentstale,精确实现和默认优化随版本:

  • 默认模式通常由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=releasedelete
  • 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代理重试边界。