一个永远只指向可达实例的分布式注册表

Consul 维护一份存活的健康服务清单

服务注册表的价值完全取决于它有多诚实。Consul 把注册的实例收进 catalog,并给它们挂上健康检查,于是「某个服务的健康实例在哪」这个问题的答案会随现实变化。注册本身很简单;检查才是全部的产品价值,因为一份列出死实例的注册表比静态清单更糟——它会主动把流量送进坑里。 这个答案的新鲜程度,取决于两个第一周之后没人再看的选择:检查多久跑一次,以及它到底测的是什么。每 30 秒跑一次的检查会瞎 30 秒;只证明进程存在的检查则永远瞎着。 注册,以及两个层级的检查 实例通过配置文件或注册 API 登记名字、地址、端口以及一条或多条检查。检查默认以 critical 状态起步,这是刻意的:在健康至少被验证过一次之前,服务无法进入可用池——想要初始就是 passing,必须显式写出来。检查的状态由它自己判定:脚本检查按退出码决定(0 为通过、非 0 为 critical),HTTP 检查按响应码决定,输出超过 4KB 会被截断,脚本检查默认 30 秒超时。也就是说,「检查通过」这几个字的含义完全取决于你写的那条命令。 同一台节点上有两个层级的检查。serfHealth 这样的节点级检查服务名为空,描述的是这台机器;service:web-1 这样的服务级检查绑定在某一条注册上。它们各自独立失败,但只取健康的过滤器要求节点上所有检查都通过,所以节点级检查变 critical 会一次带走这台机器上的所有服务,而服务级检查变 critical 只移除一个实例。 调用方实际拿到什么 catalog 会回答两个不同的问题。不加过滤时,/v1/health/service/web 返回全部注册以及每条检查的状态;加上 passing=true 时,只返回所有检查都通过的实例。DNS 与 SRV 永远按第二种方式回答,所以解析器不会拿到一个不能用的名字,而这个通过集合就是负载均衡池。 catalog 同时也是最终一致的,机制叫反熵。注册是即时的——agent 会通知 leader——但修复是周期性的:每个 agent 按一个间隔把自己的本地状态与 catalog 对齐,128 个节点以内的集群是 1 分钟,随集群规模增长而变长,并且每个 agent 的起始时间都会错开,以免所有人同时同步。同步失败只会被记进日志、等下一个周期重试,所以一条错误的 catalog 记录可以安静地存活长达一个间隔。读路径也有自己的取舍:默认情况下 agent 会把 catalog 读请求转发给 leader,并用一个假定领导地位短期稳定的 leader lease,因此很快但偶尔可能返回陈旧结果;consistent 模式要求 leader 先向多数派确认自己仍是 leader,stale 模式则允许任何 server 不做检查就回答。这三档没有对错,只有你愿意为一个多新的答案付出多少延迟。 ...

四月 6, 2028 · 1 分钟 · XB Field Notes