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

两种模式:

  • 客户端发现:客户端直接查注册中心,自行选择实例(Eureka)
  • 服务端发现:通过负载均衡器转发,LB 查注册中心(Consul + Fabio/Nginx)

2. 组件对比

特性EurekaConsulNacosetcd
协议HTTPHTTP/DNSHTTP/gRPCgRPC/HTTP
一致性AP (Peer 复制)CP (Raft)AP/CP 可切换CP (Raft)
健康检查客户端心跳TCP/HTTP/脚本TCP/HTTP/MySQL租约 (Lease)
多数据中心需配置原生支持支持
K8s 集成一般原生(CoreDNS)
语言JavaGoJavaGo
Spring Cloud原生支持原生需适配

3. Nacos 实践

3.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);
    }
}

3.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);
    }
}

3.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;
}

4. Consul 实践

4.1 服务注册

{
  "service": {
    "name": "web-api",
    "tags": ["v1", "primary"],
    "port": 8080,
    "check": {
      "http": "http://localhost:8080/health",
      "interval": "10s",
      "timeout": "5s"
    }
  }
}

4.2 DNS 方式发现

# Consul 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

5. etcd 服务发现

import etcd3

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

# 注册服务(带租约,自动过期)
lease = client.lease(ttl=10)
client.put('/services/user-service/10.0.1.1:8080', 
           json.dumps({'host': '10.0.1.1', 'port': 8080}),
           lease=lease)

# 续约(心跳)
lease.refresh()

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

# Watch 变更
watch_iter, cancel = client.watch_prefix('/services/user-service/')
for event in watch_iter:
    print(f"Event: {event}")

6. 健康检查策略

类型方式优点缺点
客户端心跳服务主动发送心跳简单假死时仍发心跳
服务端探测注册中心主动探测可靠增加注册中心负载
双向心跳双向检测最可靠复杂度高
// Spring Boot Actuator 健康检查
@Component
public class DatabaseHealthIndicator implements HealthIndicator {
    @Override
    public Health health() {
        if (database.isConnected()) {
            return Health.up().build();
        }
        return Health.down().withDetail("error", "DB connection failed").build();
    }
}

总结

  • Spring Cloud 生态:首选 Nacos(注册+配置一体)
  • 多语言/跨平台:Consul(DNS 支持 + 多数据中心)
  • K8s 原生:etcd + CoreDNS
  • 遗留系统:Eureka(稳定但已停更,建议迁移)

继续阅读

探索更多技术文章

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

全部文章 返回首页

「distributed-systems」更多文章

  1. 分布式高可用架构模式:多活、容灾、降级与 K8s 编排高可用
  2. 分布式链路追踪实战:OpenTelemetry、Jaeger 与 W3C Trace Context
  3. 分布式缓存深度策略:Redis Cluster、一致性哈希与多级缓存架构