Karp 的技术博客

Consul 是什么?从零理解配置中心与服务发现

写给自己的 Consul 入门笔记。搞清楚"它是什么、解决什么问题、怎么工作的"。

一、背景:没有 Consul 之前

假设你有三个服务:订单服务用户服务支付服务

问题 1:配置怎么管?

# 每个服务各自写死配置
DB_URL=postgres://localhost:5432/order
REDIS_ADDR=localhost:6379
PAY_SECRET=abc123

改一个配置 → 要重新部署 → 所有服务都要改自己的配置文件 → 乱。

问题 2:服务之间怎么找到对方?

# 订单服务硬编码调用用户服务
user_service_url = "http://192.168.1.10:8080"

用户服务换机器了 → 订单服务也要改代码重新部署 → 乱。

Consul 就是来解决这两个问题的。


二、Consul 是什么

Consul 是 HashiCorp 公司用 Go 语言写的一个开源工具,核心能力三件套:

能力解决什么问题
服务注册与发现服务自动上报自己在哪,其他人自动找到它
配置中心(KV Store)配置集中存储,修改实时推送,不用重启服务
健康检查自动踢掉挂掉的服务,只返回健康的实例

一句话:Consul 是分布式系统的"电话本 + 公告栏"


三、核心概念

3.1 Agent

Consul 以 Agent 进程的形式运行,有两种角色:

┌─────────────────────────────────────┐
│              Consul 集群             │
│                                     │
│   ┌─────────┐    ┌─────────┐        │
│   │ Server  │◄──►│ Server  │  ← 存储数据,做决策(通常 3 或 5 个)
│   │ (Leader)│    │         │        │
│   └────┬────┘    └─────────┘        │
│        │                            │
│   ┌────▼────┐   ┌─────────┐        │
│   │ Client  │   │ Client  │  ← 每台业务机器上跑,转发请求
│   └────┬────┘   └────┬────┘        │
└────────┼─────────────┼─────────────┘
         │             │
    [订单服务]      [用户服务]
  • Server:负责存数据、共识投票(Raft 协议),通常部署 3 或 5 个保证高可用
  • Client:轻量代理,跑在业务机器上,负责转发请求给 Server

3.2 Raft 共识算法(为什么 Consul 的数据是可信的)

Consul Server 之间用 Raft 算法保证一致性:

写操作流程:

Client 发写请求
  → 打到任意 Server
  → 转发给 Leader
  → Leader 写入本地 Log
  → 同步给超过半数的 Follower(如 3 节点需 2 个确认)
  → 确认后 Commit
  → 返回成功给 Client

关键点:只要多数节点存活,数据就不会丢。3 节点集群可以容忍 1 个节点挂掉。


四、配置中心原理

4.1 数据模型

Consul 的配置存在 KV Store 里,就是一个树形的键值对:

config/
├── app/
│   ├── db-url          = "postgres://..."
│   ├── redis-addr      = "localhost:6379"
│   └── pay-secret      = "abc123"
└── feature-flags/
    └── new-checkout    = "false"

4.2 读配置

# HTTP API 读
curl http://localhost:8500/v1/kv/config/app/db-url

# 返回(Base64 编码)
{
  "Key": "config/app/db-url",
  "Value": "cG9zdGdyZXM6Ly8u...",  ← Base64("postgres://...")
  "ModifyIndex": 42                  ← 版本号,Watch 用的
}

4.3 Watch 机制(配置热更新的关键)

Consul 用长轮询(Long Polling)实现配置变更推送:

┌─────────────┐                    ┌─────────┐
│  你的服务    │                    │  Consul │
└──────┬──────┘                    └────┬────┘
       │                                │
       │  GET /v1/kv/config/app/db-url  │
       │  ?wait=60s&index=42  ─────────►│  ← 带上当前版本号,最多等 60 秒
       │                                │
       │        (配置没变,挂着等...)   │
       │                                │
       │        (管理员改了配置)        │
       │                                │
       │◄──────── 立即返回新值 ──────────│  ← 版本号变成 43
       │          index=43              │
       │                                │
       │  服务收到 → 热更新配置,不重启   │
       │                                │
       │  GET ...?wait=60s&index=43 ───►│  ← 继续 Watch 下一次变化

效果:管理员在 Consul UI 改一个配置 → 服务几秒内自动感知 → 无需重启。


五、服务发现原理

5.1 服务注册

服务启动时,向本机的 Consul Agent 注册自己:

POST /v1/agent/service/register

{
  "ID":      "user-service-1",
  "Name":    "user-service",
  "Address": "192.168.1.10",
  "Port":    8080,
  "Tags":    ["v2", "prod"],
  "Check": {
    "HTTP":     "http://192.168.1.10:8080/health",
    "Interval": "10s",
    "Timeout":  "3s"
  }
}

5.2 健康检查

Consul 定时主动探测你的服务是否存活:

三种健康检查方式:

1. HTTP Check(最常用)
   Consul 每 10s GET 你的 /health 接口
   200 → 健康 ✅
   非 200 / 超时 → 不健康 ❌

2. TCP Check
   Consul 每 10s TCP connect 你的端口
   连得上 → 健康 ✅

3. TTL Check(适合没有 HTTP 端点的 worker)
   你自己定时 PUT 心跳给 Consul
   超时没收到 → 不健康 ❌

5.3 服务查询

其他服务来查"我要调用 user-service,它在哪?":

GET /v1/health/service/user-service?passing=true

# 只返回健康的实例
[
  { "Service": { "Address": "192.168.1.10", "Port": 8080 } },
  { "Service": { "Address": "192.168.1.11", "Port": 8080 } }
]

关键?passing=true 过滤掉不健康的实例,调用方只会拿到可用的地址。

5.4 完整流程图

[用户服务] 启动
    │
    ▼
向 Consul 注册(IP:Port + 健康检查地址)
    │
    ▼
Consul 每 10s 检查 /health ──→ 正常:标记 passing
                              挂掉:标记 critical,从列表移除

[订单服务] 要调用用户服务
    │
    ▼
问 Consul:"user-service 有哪些健康实例?"
    │
    ▼
Consul 返回健康实例列表
    │
    ▼
订单服务自己选一个(随机 / 轮询)→ 发起调用

六、本地快速体验

6.1 Docker 一条命令启动

docker run -d \
  --name consul \
  -p 8500:8500 \
  consul:latest agent -dev -ui -client=0.0.0.0

打开 http://localhost:8500 → 可以看到 Web UI。

6.2 写一个配置

# 写入
curl -X PUT -d 'postgres://localhost:5432/mydb' \
  http://localhost:8500/v1/kv/config/app/db-url

# 读取
curl http://localhost:8500/v1/kv/config/app/db-url?raw
# 输出:postgres://localhost:5432/mydb

6.3 注册一个服务(模拟)

curl -X PUT -d '{
  "ID": "user-svc-1",
  "Name": "user-service",
  "Address": "127.0.0.1",
  "Port": 8080,
  "Check": {
    "TTL": "30s"
  }
}' http://localhost:8500/v1/agent/service/register

在 UI 的 Services 页面里就能看到它了。


七、Go 接入代码

package main

import (
    "fmt"
    "log"
    "github.com/hashicorp/consul/api"
)

func main() {
    // 连接本地 Consul(默认 localhost:8500)
    client, err := api.NewClient(api.DefaultConfig())
    if err != nil {
        log.Fatal(err)
    }

    // ── 配置中心:读配置 ──────────────────────────
    kv := client.KV()
    pair, _, err := kv.Get("config/app/db-url", nil)
    if err != nil {
        log.Fatal(err)
    }
    fmt.Println("DB URL:", string(pair.Value))

    // ── 服务发现:查询健康实例 ────────────────────
    health := client.Health()
    services, _, err := health.Service("user-service", "", true, nil)
    if err != nil {
        log.Fatal(err)
    }
    for _, s := range services {
        fmt.Printf("发现实例: %s:%d\n", s.Service.Address, s.Service.Port)
    }
}

八、和其他方案对比

ConsuletcdNacos
定位服务发现 + 配置分布式 KV微服务治理
语言GoGoJava
内存占用~30MB~50MB~300MB
内置健康检查✅ 丰富⚠️ 仅 TTL✅ 丰富
Web UI
适合场景无 K8s 本地/中小集群K8s 内部Java 生态

九、总结

Consul 解决的核心问题:

配置中心
  服务不用重启就能感知配置变化
  → KV Store + 长轮询 Watch

服务发现
  服务不用硬编码对方地址
  → 注册 + 健康检查 + 查询

数据可靠性
  多节点不丢数据
  → Raft 共识算法

本地开发首选 Consul:一个 Docker 命令,UI、配置、服务发现全有,不依赖 Java,Go 原生支持。


参考:Consul 官方文档

golang

版权属于:karp
作品采用:本作品采用 知识共享署名-相同方式共享 4.0 国际许可协议 进行许可。
更新于: 2026年05月22日 09:30
0

目录

来自 《Consul 入门:配置中心与服务发现》