← 返回文章列表

Consul

记录 Consul 的服务发现、健康检查与基础使用。

Consul 原有学习记录

服务少的时候,可以在配置里直接写地址。实例多起来,地址会变,某个实例还可能暂时不可用,这时就需要一个地方记录“这个服务现在有哪些实例”。Consul 可以提供服务注册、发现和健康检查等能力。

1、注册和发现是什么?

  • 注册:把实例 ID、服务名、地址和端口告诉 Consul。
  • 发现:按服务名查询符合条件的实例,再选择一个进行调用。
  • 健康检查:记录实例状态,让调用方能够过滤异常实例。

“已注册”和“业务可用”要分开。消费者要查询健康状态,而且调用仍然需要超时和错误处理;健康检查不能保证下一次请求必然成功。相关接口见 Health API。

2、安装和启动

这里用 Docker 启动一个本地开发实例。示例固定在 1.21.3,方便对照旧学习环境,不代表推荐生产环境停留在该版本。

docker run -d \
  --name dev-consul \
  -p 127.0.0.1:8500:8500 \
  hashicorp/consul:1.21.3 agent -dev -client=0.0.0.0

浏览器访问 http://127.0.0.1:8500。-dev 是开发模式,退出后不能依靠它保存生产状态。容器里监听所有地址,是为了让端口映射能访问;宿主机映射仍然限制在本地。

Consul 启动记录

Consul 界面记录

3、在 Go 中完成一次注册与发现

先在新的示例目录初始化模块:

go mod init example.com/consul-demo

下面保存为 main.go,再执行后面的依赖安装指令。

package main

import (
	"fmt"
	"net/http"
	"os"
	"time"

	"github.com/hashicorp/consul/api"
)

func run() error {
	cfg := api.DefaultConfig()
	cfg.Address = "127.0.0.1:8500"
	cfg.HttpClient = &http.Client{Timeout: 3 * time.Second}
	client, err := api.NewClient(cfg)
	if err != nil {
		return err
	}
	registration := &api.AgentServiceRegistration{
		ID: "demo-api-1", Name: "demo-api",
		Address: "127.0.0.1", Port: 18080,
		Check: &api.AgentServiceCheck{TTL: "15s"},
	}
	if err := client.Agent().ServiceRegister(registration); err != nil {
		return err
	}
	defer func() {
		if err := client.Agent().ServiceDeregister(registration.ID); err != nil {
			fmt.Println("注销失败:", err)
		}
	}()
	if err := client.Agent().UpdateTTL("service:demo-api-1", "demo heartbeat", "passing"); err != nil {
		return err
	}
	services, _, err := client.Health().Service("demo-api", "", true, nil)
	if err != nil {
		return err
	}
	for _, entry := range services {
		fmt.Printf("%s %s:%d\n", entry.Service.ID, entry.Service.Address, entry.Service.Port)
	}
	return nil
}

func main() {
	if err := run(); err != nil {
		fmt.Fprintln(os.Stderr, "Consul 示例失败:", err)
		os.Exit(1)
	}
}

保存代码后,安装示例使用的 SDK 版本并运行:

go get github.com/hashicorp/consul/api@v1.30.0
go mod tidy
go run .

这个版本用于复现上面的 API 示例;后续更换版本时重新验证,并保留 go.mod 和 go.sum。程序演示“注册 → 更新 TTL 心跳 → 查询健康实例 → 退出时注销”。它没有启动 18080 上的真实业务服务,地址只是示例元数据;打印出地址不等于业务接口已经可用了。Go SDK 接口与服务注册 API可以对照着看。

真正接到应用里,先启动业务服务,再注册可达地址。TTL 心跳要周期性更新,并且结合自己的状态决定报 passing 还是 critical;如果希望 Consul 主动检查接口,可以换成 HTTP 检查,确认 Consul agent 能访问对应地址。

4、容易踩的坑

服务名和实例 ID 不一样。多个实例可以都叫 demo-api,但 ID 应该各不相同,否则会覆盖注册信息。

地址是给消费者用的。这里的 127.0.0.1 只适合同一台宿主机上的调用方。跨机器或跨容器调用时,要注册调用方实际能访问的地址。

注销不是故障处理的全部。正常退出可以注销,进程被强制结束时还要依靠健康检查。发现时查询健康实例,调用时继续处理超时与连接失败。

KV 存储也不是随便换个数据库。可以用于配置等场景,但要考虑权限、更新通知和读取失败时的行为。

参考资料

Consul 文档、健康检查、1.21.3 版本记录。

关于这篇文章的日期

旧日期 2025-01-16 与文章发布记录不符,现按旧站可确认的首次发布日 2026-01-20 整理。实际创作日期尚无法确认。