07. 服务注册与发现

服务注册与发现机制详解:Eureka、Consul、Nacos、etcd 对比与健康检查策略

微服务架构中,服务实例随着流量变化动态扩缩容,客户端再也无法像单体应用那样将目标地址硬编码在配置文件里。服务注册与发现机制正是为了解决这一矛盾而生的核心基础设施:它让服务实例在启动时自动将自己的网络地址注册到注册中心,并在关闭时自动注销;调用方则通过注册中心实时查询可用实例列表,实现服务间的松耦合动态寻址。没有这一层抽象,扩容操作将变成一场手工修改配置文件的灾难,故障转移也只能依赖运维人工介入。

1. 核心模型

服务注册:
  Service A (Instance1: 10.0.1.1:8080) ──register──→ Registry
  Service A (Instance2: 10.0.1.2:8080) ──register──→ Registry

服务发现:
  Client ──query "Service A"──→ Registry ──返回 [10.0.1.1:8080, 10.0.1.2:8080]──→ Client

两种模式:

  • 客户端发现(Client-Side Discovery):客户端直接查询注册中心获取实例列表,并在本地执行负载均衡策略选择目标实例发起调用。代表方案有 Netflix Eureka 配合 Ribbon、Nacos 配合 Spring Cloud LoadBalancer。
  • 服务端发现(Server-Side Discovery):客户端请求先经过一个负载均衡器或代理网关,由该中间层负责查询注册中心并选择后端实例,客户端完全无感知。代表方案有 Kubernetes Service + kube-proxy、Consul + Fabio/Nginx。

客户端发现的优势在于架构轻量、网络跳数少、响应延迟低;缺点是每个客户端都要集成服务发现 SDK 和负载均衡逻辑,语言栈难以统一。服务端发现将复杂性收拢到基础设施层,客户端只需发 HTTP 请求即可,但引入了一个额外的网络跳转,也对代理层的可用性提出了更高要求。在生产环境中,中小型团队通常从客户端发现起步,随着集群规模扩大和多语言需求出现,逐步迁移到服务端发现甚至 Service Mesh 模式。

2. 服务发现架构模式

2.1 客户端发现模式

客户端发现模式下,服务消费者自身承担服务查询和实例选择的职责。这种模式的典型调用链路为:应用启动时向注册中心注册自身 / 应用运行中缓存一份目标服务的实例列表 / 请求到来时从本地缓存中按负载均衡策略选出一个实例 / 直连目标实例发起调用。Netflix OSS 生态中的 Eureka Client + Ribbon 就是这一模式的经典实现。客户端发现的好处是少了一层中间代理,整体链路更短;但缺点是客户端必须引入发现 SDK,且不同语言需要各自维护一套实现,增加了技术异构成本。

// 客户端发现示例:Spring Cloud + Nacos
@Service
public class ClientSideDiscoveryService {
    @Autowired
    private DiscoveryClient discoveryClient;
    @Autowired
    private RestTemplate restTemplate;

    public String invokeOrderService() {
        // 从注册中心拉取 order-service 的所有实例
        List<ServiceInstance> instances = discoveryClient.getInstances("order-service");
        if (instances.isEmpty()) {
            throw new RuntimeException("No available order-service instance");
        }
        // 本地轮询负载均衡
        ServiceInstance instance = instances.get(
            new Random().nextInt(instances.size())
        );
        String url = instance.getUri() + "/orders";
        return restTemplate.getForObject(url, String.class);
    }
}

2.2 服务端发现模式

服务端发现模式将服务查询和负载均衡的职责从客户端剥离,交由专门的中间代理或负载均衡器完成。客户端只需向一个固定的虚拟地址或服务名发起请求,代理组件负责查询注册中心、执行负载均衡算法、将请求转发到合适的后端实例。这种模式对客户端零侵入,任何语言编写的服务都可以平等使用,非常契合多语言微服务场景。Kubernetes Service 基于 iptables/IPVS 的服务发现就是服务端发现的典型代表。

# Kubernetes Service 服务端发现示例
apiVersion: v1
kind: Service
metadata:
  name: order-service
spec:
  selector:
    app: order-service
  ports:
    - port: 8080
      targetPort: 8080
  # ClusterIP 模式:集群内部通过 DNS 解析到虚拟 IP,kube-proxy 做负载均衡
  type: ClusterIP
# 集群内 Pod 只需访问固定的 DNS 名称,完全无需关心后端实例变化
curl http://order-service.default.svc.cluster.local:8080/orders

2.3 Service Mesh 模式

Service Mesh 是服务端发现模式的进一步演进,它将服务发现、负载均衡、熔断、重试、TLS 加密、可观测性等通信治理能力下沉到 Sidecar 代理层,业务代码彻底与网络通信解耦。每个 Pod 或容器旁边都运行一个轻量级代理(如 Istio 中的 Envoy),由控制平面(如 Istio Pilot/istiod)统一下发服务发现配置和流量规则。客户端发现与服务端发现的边界在这里被模糊化:对于应用而言,它仍然是发一个本地请求到 localhost,看起来像是客户端发现;但实际的流量调度和服务寻址完全由代理控制,又具备服务端发现的特征。

# Istio DestinationRule:定义服务发现后的负载均衡策略
apiVersion: networking.istio.io/v1beta1
kind: DestinationRule
metadata:
  name: order-service
spec:
  host: order-service
  trafficPolicy:
    loadBalancer:
      simple: LEAST_CONN
    outlierDetection:
      consecutive5xxErrors: 3
      interval: 10s
      baseEjectionTime: 30s

Service Mesh 的引入显著提升了系统的可观测性和流量治理能力,但也带来了运维复杂度和资源开销的双重挑战。每个 Sidecar 通常需要几十到上百兆内存,成千上万个 Pod 的资源累加不容小觑。建议在具备成熟运维能力的团队以及需要精细化流量控制的场景下采用。

3. 组件对比

特性EurekaConsulNacosetcd
协议HTTPHTTP/DNSHTTP/gRPCgRPC/HTTP
一致性AP (Peer 复制)CP (Raft)AP/CP 可切换CP (Raft)
健康检查客户端心跳TCP/HTTP/脚本TCP/HTTP/MySQL租约 (Lease)
多数据中心需额外配置原生 WAN gossip 支持支持无内建支持
K8s 集成一般好(Consul on K8s)好原生(CoreDNS 后端)
语言JavaGoJavaGo
Spring Cloud原生Spring Cloud ConsulSpring Cloud Alibaba需 spring-cloud-starter-etcd
配置中心Config Server(独立)KV 存储原生一体化需配合第三方

选型时,如果你的团队深耕 Java/Spring Cloud 生态且追求一站式解决方案,Nacos 的注册发现与配置管理一体化设计能显著降低认知成本。如果你的系统需要覆盖多种编程语言,且跨地域部署多个数据中心,Consul 的 DNS 接口和原生 WAN gossip 机制更具优势。如果你已经在 Kubernetes 上全面容器化,那么 etcd + CoreDNS 的声明式服务发现就是最直接的选择。Eureka 2.x 停止开发,仅建议在存量系统中维持使用,新项目应优先考虑 Nacos 或 Consul 替代。

4. Nacos 实践

4.1 服务注册

# application.yml
spring:
  application:
    name: order-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        namespace: dev
        group: DEFAULT_GROUP
        metadata:
          version: v1
          region: cn-beijing
@SpringBootApplication
@EnableDiscoveryClient
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

4.2 服务发现与调用

@Service
public class OrderService {

    @Autowired
    private LoadBalancerClient loadBalancer;

    @Autowired
    private RestTemplate restTemplate;

    public User getUser(Long userId) {
        // 方式1:使用 LoadBalancerClient
        ServiceInstance instance = loadBalancer.choose("user-service");
        String url = instance.getUri() + "/users/" + userId;
        return restTemplate.getForObject(url, User.class);

        // 方式2:使用 @LoadBalanced RestTemplate
        return restTemplate.getForObject(
            "http://user-service/users/{id}", User.class, userId);
    }
}

4.3 配置中心

# bootstrap.yml
spring:
  cloud:
    nacos:
      config:
        server-addr: 127.0.0.1:8848
        file-extension: yaml
        group: DEFAULT_GROUP
        namespace: dev
@RestController
@RefreshScope  // 配置变更自动刷新
public class ConfigController {
    @Value("${app.timeout:3000}")
    private int timeout;
}

Nacos 最突出的优势是将服务注册发现与配置管理整合在同一套服务端中,集群共享、Namespace 隔离、Group 分组等概念在两个模块中保持高度一致。对于中小型团队而言,这减少了维护两套集群的运维负担。Nacos 的临时实例通过客户端主动心跳维持存活,持久化实例则适合代理型服务或第三方中间件的注册场景。在阿里云上,MSE Nacos 提供了托管服务,免去了自建集群的运维复杂性。

5. Consul 深度实战

Consul 由 HashiCorp 用 Go 语言开发,是服务发现领域功能最全面的方案之一。它内建了服务注册发现、健康检查、KV 存储、安全访问控制和多数据中心联邦等能力,既可以作为独立注册中心部署,也可以无缝嵌入 Kubernetes 环境。

5.1 服务注册

Consul 支持多种服务注册方式:Agent 本地配置文件、HTTP API 调用、以及 Kubernetes 的自动同步。

// /etc/consul.d/web-api.json (Agent 配置文件方式)
{
  "service": {
    "name": "web-api",
    "tags": ["v1", "primary", "api"],
    "port": 8080,
    "meta": {
      "version": "1.2.3",
      "region": "ap-northeast-1"
    },
    "check": {
      "http": "http://localhost:8080/health",
      "interval": "10s",
      "timeout": "5s",
      "deregister_critical_service_after": "1m"
    }
  }
}
# 重新加载配置使注册生效
consul reload

# 或者通过 HTTP API 动态注册
curl -X PUT http://localhost:8500/v1/agent/service/register \
  -H "Content-Type: application/json" \
  -d '{
    "ID": "web-api-001",
    "Name": "web-api",
    "Tags": ["v1"],
    "Port": 8080,
    "Check": {
      "HTTP": "http://10.0.1.10:8080/health",
      "Interval": "10s"
    }
  }'

deregister_critical_service_after 参数尤为关键:当健康检查连续失败达到指定时长后,Consul 会自动将该实例从服务列表中移除,避免将请求路由到已故障的节点。

5.2 健康检查

Consul 的健康检查机制远比简单的心跳丰富。它支持 HTTP 检查、TCP 端口探测、GRPC 健康检查、脚本执行以及 TTL(服务主动上报存活)五种模式。生产环境中,HTTP 检查最为常用,建议健康检查端点 (/health) 应综合验证服务核心依赖(数据库连接、缓存状态、外部 API 可达性),而非简单地返回 HTTP 200,否则容易出现服务假死但健康检查仍通过的盲区。

# /etc/consul.d/checks.hcl
# 多维度健康检查配置示例
check {
  id       = "web-api-health"
  name     = "Web API HTTP Health"
  http     = "http://localhost:8080/actuator/health"
  method   = "GET"
  interval = "15s"
  timeout  = "5s"
}

check {
  id       = "redis-connectivity"
  name     = "Redis Connection Check"
  script   = "/usr/local/bin/check-redis.sh"
  interval = "30s"
  timeout  = "10s"
}
#!/usr/bin/env bash
# /usr/local/bin/check-redis.sh — Redis 连通性检查脚本
redis-cli -h redis.internal -p 6379 ping | grep -q PONG
if [ $? -eq 0 ]; then
    exit 0
else
    exit 2
fi

5.3 KV 存储

Consul 的 KV 存储功能常被低估,实际上它能胜任配置管理、Leader 选举、Feature Flag 等多种场景。KV 路径以树形结构组织,天然支持 Prefix 查询,配合 ModifyIndex 还可以实现 CAS(Compare-And-Swap)乐观锁,是构建分布式协调逻辑的有力工具。

# 写入配置
consul kv put app/config/database_url "postgres://db.internal:5432/myapp"
consul kv put app/config/max_connections "100"

# 读取配置
consul kv get app/config/database_url

# 以 JSON 格式获取完整元数据(含 CreateIndex、ModifyIndex)
consul kv get -detailed app/config/database_url

# 批量查询前缀
consul kv get -recurse app/config/

# 删除
consul kv delete app/config/max_connections
# Python 使用 consul 库操作 KV
import consul

c = consul.Consul(host='localhost', port=8500)

# 写入
c.kv.put('app/feature/enable_new_ui', 'true')

# 带 Cas 参数的安全更新(防止并发写覆盖)
index, data = c.kv.get('app/feature/enable_new_ui')
c.kv.put('app/feature/enable_new_ui', 'false', cas=index['ModifyIndex'])

# Watch 监听变更
index = None
while True:
    index, data = c.kv.get('app/feature/enable_new_ui', index=index, wait='30s')
    if data:
        print(f"Config changed: {data['Value'].decode()}")

5.4 ACL 安全访问控制

在多租户或混合云部署中,不应让所有服务都能无限制地读写 Consul 的任何数据。Consul ACL 系统通过 Token、Policy 和 Role 三层模型实现精细化权限管控。

# acl-policy.hcl — 定义只允许读取 web-api 服务的策略
service_prefix "web-api" {
  policy = "read"
}

node_prefix "" {
  policy = "read"
}

key_prefix "app/config/" {
  policy = "read"
}
# 启用 ACL(Consul 主配置)
cat <<EOF >> /etc/consul/consul.hcl
acl {
  enabled = true
  default_policy = "deny"
  enable_token_persistence = true
}
EOF

# 创建策略
consul acl policy create -name "web-api-reader" -rules @acl-policy.hcl \
  -token "your-bootstrap-token"

# 创建 Token 并绑定策略
consul acl token create -description "Web API Service Token" \
  -policy-name "web-api-reader" \
  -token "your-bootstrap-token"

启用 default_policy = “deny” 后,所有未携带合法 Token 的请求均被拒绝。服务注册时应为每个微服务签发最小权限的 Token,遵循最小权限原则,降低因 Token 泄露导致的横向移动风险。

6. etcd 与 Kubernetes 服务发现

6.1 etcd 原生服务发现

etcd 是 CoreOS 开发的分布式键值存储,基于 Raft 共识协议实现 CP 一致性。在 Kubernetes 生态中,etcd 是整个集群的 “唯一事实来源”,所有 API Server 的资源对象(Pod、Service、EndpointSlice)都持久化于 etcd。etcd 的 Lease(租约)机制天然适合用于临时服务注册:客户端注册时绑定一个 TTL 租约,必须周期性续约以维持键值存在;一旦客户端崩溃或网络隔离,租约到期后服务端自动清理失效键,无需额外的注销逻辑。

import etcd3
import json
import time

client = etcd3.Client(host='localhost', port=2379)

# 创建 10 秒租约
lease = client.lease(ttl=10)

# 注册服务(键带服务名前缀,值存 JSON 元数据)
client.put(
    '/services/user-service/10.0.1.1:8080',
    json.dumps({
        'host': '10.0.1.1',
        'port': 8080,
        'weight': 1,
        'version': 'v1',
        'health_path': '/health'
    }),
    lease=lease
)

# 在独立线程中维持心跳(续租)
def keep_alive():
    while True:
        try:
            lease.refresh()
            time.sleep(5)
        except Exception:
            break

# 发现服务
instances = client.get_prefix('/services/user-service/')
for value, metadata in instances:
    info = json.loads(value.decode())
    addr = f"{info['host']}:{info['port']}"
    print(f"Found instance: {addr}")

# Watch 实时监听变更
watch_iter, cancel = client.watch_prefix('/services/user-service/')
for event in watch_iter:
    key = event.key.decode()
    if isinstance(event, etcd3.events.PutEvent):
        print(f"[ADD/UPDATE] {key}")
    elif isinstance(event, etcd3.events.DeleteEvent):
        print(f"[REMOVE] {key}")

6.2 Kubernetes 服务发现

Kubernetes 中的服务发现采用声明式服务端发现模式。开发者只需创建一个 Service 资源定义其选择器(selector),控制平面就会自动维护对应的 Endpoint 或 EndpointSlice 对象,将匹配到的 Pod IP 和端口信息写入 etcd。集群 DNS(CoreDNS)同步监听这些资源变化,当 Pod 查询 order-service.default.svc.cluster.local 时,CoreDNS 返回该 Service 的 ClusterIP 地址;流量到达 ClusterIP 后,由 kube-proxy 通过 iptables 或 IPVS 规则将请求负载均衡到各个后端 Pod。

# 完整的 K8s 服务发现示例
apiVersion: v1
kind: Service
metadata:
  name: order-service
  namespace: default
spec:
  selector:
    app: order-service
  ports:
    - name: http
      port: 8080
      targetPort: 8080
  type: ClusterIP
---
apiVersion: discovery.k8s.io/v1
kind: EndpointSlice
metadata:
  name: order-service-abc
  namespace: default
  labels:
    kubernetes.io/service-name: order-service
addressType: IPv4
ports:
  - name: http
    protocol: TCP
    port: 8080
endpoints:
  - addresses:
      - "10.1.2.3"
    conditions:
      ready: true
  - addresses:
      - "10.1.2.4"
    conditions:
      ready: true
# Pod 内查看 DNS 解析结果
nslookup order-service.default.svc.cluster.local

# 查看 Service 对应的 EndpointSlice(K8s 1.21+ 默认)
kubectl get endpointslices -l kubernetes.io/service-name=order-service

# 查看 kube-proxy 的 iptables 规则(使用 iptables 模式时)
iptables -t nat -L KUBE-SVC-XXXXXXXXXX -n --line-numbers

K8s 的 Headless Service(clusterIP: None)是一种特殊模式:DNS 直接返回所有匹配 Pod 的 A 记录列表,将负载均衡决策权交还给客户端。这在有状态服务(如数据库集群的分片连接)或需要客户端感知拓扑的场景中非常有用。

7. Eureka、Spring Cloud 与 Consul 对比详解

7.1 Eureka 的设计与局限

Eureka 是 Netflix 开源的服务注册中心,采用纯 AP 设计,各节点间通过异步复制同步注册表信息。这种设计在分区容忍性上表现优异,即使网络分区也能继续提供读写服务,但会面临短暂的数据不一致窗口。Eureka Server 的自我保护机制会在大规模实例同时失联时(如网络抖动)自动进入保护模式,拒绝剔除任何实例,这一设计初衷是避免因为注册中心自身问题导致服务雪崩,但在生产中也常被误用。Eureka 1.x 长期未获重大更新,Spring Cloud Netflix Eureka 也进入维护模式,官方已明确建议新项目迁移至 Nacos 或 Consul。

// Eureka Client 自定义注册元数据示例
@EnableEurekaClient
@SpringBootApplication
public class LegacyServiceApp {
    public static void main(String[] args) {
        SpringApplication.run(LegacyServiceApp.class, args);
    }

    @Bean
    public EurekaInstanceConfigBean eurekaInstanceConfig() {
        EurekaInstanceConfigBean config = new EurekaInstanceConfigBean(
            new InetUtils(new InetUtilsProperties()));
        config.getMetadataMap().put("team", "platform");
        config.getMetadataMap().put("version", "1.4.0");
        return config;
    }
}

7.2 Spring Cloud Consul

Spring Cloud Consul 为 Consul 提供了与 Eureka 同等丝滑的 Spring Cloud 集成体验。通过 spring-cloud-starter-consul-discovery 依赖,应用启动时自动向 Consul Agent 注册自身,并通过 @LoadBalanced RestTemplate 或 FeignClient 透明地发现目标服务。

# application.yml — Spring Cloud Consul 配置
spring:
  application:
    name: payment-service
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        enabled: true
        register: true
        health-check-path: /actuator/health
        health-check-interval: 15s
        tags:
          - version=2.0
          - env=production
        metadata:
          management.context-path: /actuator
@FeignClient(name = "inventory-service")
public interface InventoryClient {
    @GetMapping("/inventory/{sku}")
    Inventory getInventory(@PathVariable("sku") String sku);
}

@Service
public class PaymentService {
    @Autowired
    private InventoryClient inventoryClient;

    public void processPayment(String sku) {
        Inventory inv = inventoryClient.getInventory(sku);
        // 逻辑处理...
    }
}

Consul 在 Spring Cloud 生态中的优势还包括与 Spring Cloud Config 的无缝衔接:通过 spring.cloud.consul.config 前缀,应用可以直接从 Consul KV 中读取配置文件,并且支持实时刷新。

7.3 迁移路径建议

存量 Eureka 系统向 Consul 迁移通常采用双注册模式作为过渡:一个服务同时向 Eureka 和 Consul 注册,调用方逐步从 Eureka Client 切到 Consul Client,待全部流量迁移完成后下线 Eureka 集群。双注册期间需要关注两个注册中心间的元数据格式差异,以及健康检查 URL 的路径一致性。

8. DNS-based 服务发现

DNS 是人类最早发明的服务发现机制,直到今天依然是互联网基础设施中最通用、最无侵入的寻址方式。在微服务内部,基于 DNS 的服务发现同样适用:服务名作为域名解析,底层由注册中心维护 A 记录或 SRV 记录,客户端无需引入任何 SDK。

# Consul DNS 查询(Consul Agent 默认在 8600 端口提供 DNS 服务)
dig @127.0.0.1 -p 8600 web-api.service.consul

# SRV 记录返回服务地址 + 端口 + 权重
dig @127.0.0.1 -p 8600 web-api.service.consul SRV

# 带数据中心的跨 DC 查询
dig @127.0.0.1 -p 8600 web-api.service.dc2.consul
# CoreDNS + etcd 实现 DNS 服务发现的典型配置
# Corefile 片段
etcd {
    fallthrough
    path /skydns
    endpoint http://localhost:2379
}

在一些严格限制外部依赖的环境中(如某些嵌入式系统或遗留基础设施),基于 DNS 的服务发现是几乎唯一可行的方案。它的主要局限在于 DNS 记录的 TTL 缓存会导致实例上下线后的生效延迟,且 SRV 记录的负载均衡能力有限,无法实现精细的权重、灰度等高级策略。因此,DNS-based 发现通常作为兜底或辅助方案,与 HTTP API 查询结合使用。

9. 多数据中心与 Federation

随着业务全球化,单一注册中心集群很难同时满足低延迟和高可用两个目标。多数据中心部署将注册中心拆分为多个地域自治的集群,服务优先在本地数据中心注册和发现,跨数据中心的调用则通过广域网链路。

9.1 Consul 多数据中心

Consul 原生支持多数据中心联邦(WAN Gossip):每个数据中心运行独立的 Consul Server 集群,Server Leader 之间通过 WAN 接口互相 gossip 同步服务目录。服务消费者可以通过 .service.<datacenter>.consul 的 DNS 后缀显式访问其他数据中心的实例。

# dc1 主数据中心 Server 配置
datacenter = "dc1"
server     = true
bootstrap_expect = 3

retry_join_wan = ["10.1.0.1", "10.2.0.1", "10.3.0.1"]

# 启用跨 DC  ACL 复制
acl {
  enabled                  = true
  default_policy           = "deny"
  enable_token_replication = true
}
# 查询 dc2 数据中心中的 web-api 服务
curl "http://localhost:8500/v1/catalog/service/web-api?dc=dc2"

# DNS 方式跨 DC 查询
dig @127.0.0.1 -p 8600 web-api.service.dc2.consul

9.2 Nacos 多集群

Nacos 通过挂载多个 Nacos 集群到一个统一的 naming 命名空间实现地域级容灾。每个集群内部自治,集群间通过地址服务器或路由层转发请求。Nacos 2.x 引入了长连接模型,将 HTTP 1.1 的短轮询升级为基于 gRPC 的双向流推送,显著降低了推送延迟和网络开销。

# nacos/conf/cluster.conf — Nacos 集群节点列表
10.0.1.1:8848
10.0.1.2:8848
10.0.1.3:8848
# Spring Boot — 通过 endpoint 多集群配置
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 10.0.1.1:8848,10.0.1.2:8848,10.0.1.3:8848
        cluster-name: SH  # 指定本服务所属集群,优先同集群路由

在多数据中心场景中,必须清醒认识到 CAP 定理的约束:追求跨数据中心的强一致性将付出极大的延迟代价。实践中通常采用 “数据中心内 CP、数据中心间 AP” 的折中策略:单个数据中心内部使用 Raft 或 Paxos 保证数据一致,跨数据中心则允许短暂的数据不同步,最终一致性即可满足服务发现的需求。

10. 健康检查策略深度分析

类型方式优点缺点适用场景
客户端心跳服务主动发送心跳包实现简单,网络开销低假死时进程仍在发心跳内部微服务,网络稳定环境
服务端探测注册中心主动 TCP/HTTP 探测真实反映网络可达性增加注册中心负载,存在探测风暴风险对外暴露服务,网络环境复杂
双向心跳双向发送检测包,任一方向超时即判定失败最可靠,能检测网络双向隔离复杂度高,需维护双向状态机金融级高可用系统
组合检查HTTP + 依赖检查(DB/Cache)全面反映服务真实状态编写和维护成本较高核心交易链路
// Spring Boot Actuator 自定义健康检查
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
    @Autowired
    private DataSource dataSource;

    @Override
    public Health health() {
        try (Connection conn = dataSource.getConnection()) {
            if (conn.isValid(3)) {
                return Health.up()
                    .withDetail("db", "connected")
                    .withDetail("validationQuery", "SELECT 1")
                    .build();
            }
        } catch (SQLException e) {
            return Health.down()
                .withDetail("error", e.getMessage())
                .build();
        }
        return Health.down().build();
    }
}

@Component
public class RedisHealthIndicator implements HealthIndicator {
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Override
    public Health health() {
        try {
            String pong = redisTemplate.getConnectionFactory()
                .getConnection().ping();
            if ("PONG".equals(pong)) {
                return Health.up().withDetail("redis", "connected").build();
            }
        } catch (Exception e) {
            return Health.down().withDetail("error", e.getMessage()).build();
        }
        return Health.down().build();
    }
}

健康检查的配置需要权衡灵敏度和稳定性:间隔太短会产生大量探测流量,给网络和实例带来不必要的压力;间隔太长则故障发现不及时。通常 HTTP 类型检查建议间隔 10-15 秒、超时 5 秒,并配合连续失败阈值(如 3 次失败才标记为不健康)避免偶发网络抖动导致的大规模实例下线。

总结

  • Spring Cloud Java 生态:首选 Nacos,注册发现与配置管理一体化,阿里云有托管版 MSE Nacos 可选。
  • 多语言异构 / 数据中心联邦部署:Consul 是最佳选择,其 DNS 接口、丰富的健康检查和多数据中心原生支持在多技术栈企业中不可替代。
  • Kubernetes 全容器化环境:etcd + CoreDNS 是最符合云原生哲学的方案,无需额外引入注册中心组件。
  • 遗留系统维护:Eureka 在稳定运行的存量系统中可以继续使用,但所有新项目应避免再以 Eureka 作为注册中心选型。迁移路径可采用双注册过渡模式,逐步向 Consul 或 Nacos 切换。
  • 高级流量治理:当服务数量超过百级别、需要精细化流量控制(金丝雀、A/B 测试、故障注入)时,Istio 等 Service Mesh 方案值得投入评估。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「distributed-systems」更多文章

  1. 边缘计算架构与就近接入
  2. 分布式系统成本优化与容量治理
  3. 单体到分布式:遗留系统迁移与绞杀者模式