← 返回微服务学习目录

第3课: 服务注册与发现

微服务架构的神经系统 - 深入理解服务治理核心机制

学习目标:通过本课程,你将深入理解服务注册与发现的核心原理,掌握主流注册中心的使用方法,并能够设计高可用的服务发现架构。

一、服务注册与发现:微服务架构的神经系统

1.1 为什么需要服务注册与发现?

在微服务架构中,服务实例的数量和位置是动态变化的。传统的硬编码服务地址方式无法适应这种动态环境。服务注册与发现机制解决了以下核心问题:

传统硬编码方式的问题

  • 服务地址变更需要修改代码并重新部署
  • 无法自动发现新的服务实例
  • 负载均衡需要手动配置
  • 故障实例无法自动剔除
  • 扩展性差,运维复杂度高

服务注册与发现的优势

  • 动态服务发现:服务消费者无需知道服务提供者的具体地址
  • 负载均衡:自动在多个服务实例之间分配请求
  • 故障隔离:自动剔除不健康的服务实例
  • 弹性伸缩:支持服务实例的动态增减
  • 服务治理:提供完整的服务生命周期管理

1.2 核心概念深度解析

服务注册与发现架构图
┌─────────────────────────────────────────────────┐
│             服务注册中心 (Service Registry)        │
│  ┌─────────────────────────────────────────┐    │
│  │          服务实例注册表                   │    │
│  │  ┌─────────────┬─────────────┬─────────┐ │    │
│  │  │ 服务A实例1   │ 服务A实例2   │ 服务B   │ │    │
│  │  │ IP:Port     │ IP:Port     │ IP:Port │ │    │
│  │  └─────────────┴─────────────┴─────────┘ │    │
│  └─────────────────────────────────────────┘    │
└─────────────────┬─────────────────┬─────────────┘
                  │                 │
          ┌───────▼───────┐ ┌───────▼───────┐
          │   服务提供者    │ │   服务消费者    │
          │  (Service     │ │  (Service     │
          │   Provider)   │ │   Consumer)   │
          │               │ │               │
          │ 1. 注册服务    │ │ 3. 发现服务    │
          │ 2. 心跳保活    │ │ 4. 调用服务    │
          └───────────────┘ └───────────────┘

核心组件详解

二、主流注册中心深度对比分析

2.1 CAP理论在注册中心的应用

根据CAP理论,分布式系统无法同时满足一致性(Consistency)、可用性(Availability)、分区容错性(Partition Tolerance)。注册中心需要根据业务场景做出权衡。

主流注册中心CAP特性对比:
┌─────────────┬──────────┬──────────┬──────────┬─────────────┐
│ 注册中心     │ CAP选择  │ 一致性    │ 可用性    │ 适用场景     │
├─────────────┼──────────┼──────────┼──────────┼─────────────┤
│ Eureka      │ AP       │ 弱一致性  │ 高可用性  │ 电商、社交等 │
│             │          │ (最终一致)│          │ 对可用性要求高│
├─────────────┼──────────┼──────────┼──────────┼─────────────┤
│ Consul      │ CP       │ 强一致性  │ 中等可用性│ 金融、支付等 │
│             │          │ (立即一致)│          │ 对一致性要求高│
├─────────────┼──────────┼──────────┼──────────┼─────────────┤
│ Nacos       │ AP+CP    │ 可配置    │ 高可用性  │ 通用场景     │
│             │          │ 灵活选择  │          │ 兼顾一致性和可用性│
├─────────────┼──────────┼──────────┼──────────┼─────────────┤
│ Zookeeper   │ CP       │ 强一致性  │ 中等可用性│ 配置管理、分布式锁│
│             │          │ (立即一致)│          │ 对强一致性要求高│
└─────────────┴──────────┴──────────┴──────────┴─────────────┘

2.2 功能特性详细对比

功能特性详细对比表:
┌──────────────┬──────────┬──────────┬──────────┬──────────┐
│ 特性          │ Eureka   │ Consul   │ Nacos    │ Zookeeper │
├──────────────┼──────────┼──────────┼──────────┼──────────┤
│ 服务发现      │ ✅        │ ✅        │ ✅        │ ✅        │
│ 健康检查      │ 心跳     │ 多种方式  │ 多种方式  │ 会话     │
│ 多数据中心    │ ✅        │ ✅        │ ✅        │ ❌        │
│ 配置管理      │ ❌        │ ✅        │ ✅        │ ✅        │
│ 服务网格      │ ❌        │ ✅        │ ❌        │ ❌        │
│ 监控仪表板    │ ✅        │ ✅        │ ✅        │ ❌        │
│ 权限控制      ❌        │ ✅        │ ✅        │ ✅        │
│ 服务路由      │ ❌        │ ✅        │ ✅        │ ❌        │
│ 开发语言      │ Java     │ Go       │ Java     │ Java     │
│ 社区活跃度    │ 中等      │ 高       │ 高       │ 高       │
│ 部署复杂度    │ 简单      │ 中等      │ 简单      │ 复杂     │
└──────────────┴──────────┴──────────┴──────────┴──────────┘
选型建议:

三、Eureka深度解析与实战

3.1 Eureka架构原理

Eureka采用客户端-服务器架构,服务实例作为Eureka客户端,向Eureka服务器注册并发送心跳。

Eureka架构图
┌─────────────────────────────────────────────────┐
│                Eureka Server集群                 │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐ │
│  │ Eureka      │  │ Eureka      │  │ Eureka      │ │
│  │ Server 1    │  │ Server 2    │  │ Server 3    │ │
│  │ (Peer节点)   │  │ (Peer节点)   │  │ (Peer节点)   │ │
│  └─────┬───────┘  └─────┬───────┘  └─────┬───────┘ │
│        │                │                │         │
│        └────────────────┼────────────────┘         │
│                         │ 服务注册信息同步           │
└─────────────────────────┼─────────────────────────┘
                          │
          ┌───────────────▼───────────────┐
          │         Eureka Client         │
          │  ┌─────────┐  ┌─────────┐     │
          │  │ 服务A   │  │ 服务B   │     │
          │  │ 实例1   │  │ 实例1   │     │
          │  └─────────┘  └─────────┘     │
          │  ┌─────────┐  ┌─────────┐     │
          │  │ 服务A   │  │ 服务B   │     │
          │  │ 实例2   │  │ 实例2   │     │
          │  └─────────┘  └─────────┘     │
          └───────────────────────────────┘

3.2 Eureka Server集群部署

// 1. Eureka Server依赖配置
// pom.xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
</dependency>

// 2. 启动类配置
@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

// 3. 集群配置示例
// application-peer1.yml (节点1配置)
server:
  port: 8761

eureka:
  instance:
    hostname: peer1
    prefer-ip-address: true
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://peer2:8762/eureka/,http://peer3:8763/eureka/

// application-peer2.yml (节点2配置)
server:
  port: 8762

eureka:
  instance:
    hostname: peer2
    prefer-ip-address: true
  client:
    register-with-eureka: true
    fetch-registry: true
    service-url:
      defaultZone: http://peer1:8761/eureka/,http://peer3:8763/eureka/

3.3 Eureka Client详细配置

// 1. Eureka Client依赖
// pom.xml
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

// 2. 启动类配置
@SpringBootApplication
@EnableDiscoveryClient  // 启用服务发现
@EnableCircuitBreaker   // 启用熔断器
public class OrderServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(OrderServiceApplication.class, args);
    }
}

// 3. 详细配置示例
# application.yml
spring:
  application:
    name: order-service  # 服务名称,用于服务发现

server:
  port: 8081

eureka:
  instance:
    # 实例配置
    instance-id: ${spring.application.name}:${server.port}  # 实例ID
    prefer-ip-address: true  # 使用IP地址而不是主机名
    ip-address: 192.168.1.100  # 指定IP地址
    lease-renewal-interval-in-seconds: 30  # 心跳间隔,默认30秒
    lease-expiration-duration-in-seconds: 90  # 续约到期时间,默认90秒
    
    # 元数据配置
    metadata-map:
      version: 1.0.0
      environment: production
      zone: zone1
      
    # 健康检查配置
    health-check-url-path: /actuator/health
    status-page-url-path: /actuator/info
    
  client:
    # 注册中心配置
    service-url:
      defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/
    
    # 客户端行为配置
    register-with-eureka: true  # 是否注册到Eureka
    fetch-registry: true  # 是否从Eureka获取注册表
    registry-fetch-interval-seconds: 30  # 注册表获取间隔
    
    # 连接和重试配置
    eureka-server-connect-timeout-seconds: 5
    eureka-server-read-timeout-seconds: 8
    eureka-server-total-connections: 200
    eureka-server-total-connections-per-host: 50

# 健康检查配置
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics
  endpoint:
    health:
      show-details: always
      enabled: true

3.4 服务调用实战

// 1. 使用RestTemplate进行服务调用
@Configuration
public class RestTemplateConfig {
    
    @Bean
    @LoadBalanced  // 启用客户端负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

@Service
public class OrderService {
    
    @Autowired
    private RestTemplate restTemplate;
    
    /**
     * 使用服务名进行服务调用
     * Eureka会自动将服务名解析为实际的服务地址
     */
    public UserDTO getUserInfo(Long userId) {
        // 使用服务名而不是具体IP地址
        String url = "http://user-service/users/" + userId;
        
        // RestTemplate会自动进行负载均衡
        ResponseEntity<UserDTO> response = restTemplate.getForEntity(url, UserDTO.class);
        
        if (response.getStatusCode().is2xxSuccessful()) {
            return response.getBody();
        } else {
            throw new RuntimeException("获取用户信息失败: " + response.getStatusCode());
        }
    }
    
    /**
     * 带超时和重试的服务调用
     */
    public ProductDTO getProductInfo(Long productId) {
        // 创建带超时配置的RestTemplate
        RestTemplate template = new RestTemplateBuilder()
            .setConnectTimeout(Duration.ofSeconds(5))
            .setReadTimeout(Duration.ofSeconds(10))
            .build();
            
        String url = "http://product-service/products/" + productId;
        
        // 使用重试机制
        return RetryTemplate.builder()
            .maxAttempts(3)
            .fixedBackoff(1000)
            .retryOn(RuntimeException.class)
            .build()
            .execute(context -> {
                return template.getForObject(url, ProductDTO.class);
            });
    }
}

// 2. 使用Feign客户端进行声明式服务调用
@FeignClient(name = "user-service", path = "/api")
public interface UserServiceClient {
    
    @GetMapping("/users/{id}")
    UserDTO getUserById(@PathVariable("id") Long id);
    
    @PostMapping("/users")
    UserDTO createUser(@RequestBody UserCreateRequest request);
    
    @PutMapping("/users/{id}")
    UserDTO updateUser(@PathVariable("id") Long id, @RequestBody UserUpdateRequest request);
    
    @DeleteMapping("/users/{id}")
    void deleteUser(@PathVariable("id") Long id);
}

// 在服务中使用Feign客户端
@Service
public class OrderService {
    
    @Autowired
    private UserServiceClient userServiceClient;
    
    public OrderDTO createOrder(OrderCreateRequest request) {
        // 使用Feign客户端调用用户服务
        UserDTO user = userServiceClient.getUserById(request.getUserId());
        
        // 创建订单逻辑...
        return orderDTO;
    }
}

四、Nacos注册中心深度实战

4.1 Nacos架构优势

Nacos是阿里巴巴开源的动态服务发现、配置管理和服务管理平台,具有以下优势:

Nacos核心特性

  • 服务发现与服务健康检查
  • 动态配置管理
  • 动态DNS服务
  • 服务及其元数据管理
  • AP和CP模式切换

Nacos优势

  • 同时支持服务发现和配置管理
  • 支持AP和CP模式,适应不同场景
  • 提供友好的管理控制台
  • 与Spring Cloud深度集成
  • 支持权重配置和灰度发布

4.2 Nacos Server部署

// 1. 下载并启动Nacos Server
# 下载Nacos
wget https://github.com/alibaba/nacos/releases/download/2.2.3/nacos-server-2.2.3.tar.gz

# 解压并启动
tar -xzf nacos-server-2.2.3.tar.gz
cd nacos/bin

# 单机模式启动(开发环境)
sh startup.sh -m standalone

# 集群模式启动(生产环境)
sh startup.sh -m cluster

// 2. Nacos集群配置
# cluster.conf
192.168.1.101:8848
192.168.1.102:8848
192.168.1.103:8848

# application.properties
server.port=8848
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8
&connectTimeout=1000&socketTimeout=3000&autoReconnect=true
db.user=root
db.password=root

4.3 Nacos Client配置

// 1. Nacos Client依赖
// pom.xml
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    <version>2022.0.0.0</version>
</dependency>

// 2. 配置示例
# application.yml
spring:
  application:
    name: user-service
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.101:8848,192.168.1.102:8848,192.168.1.103:8848
        namespace: dev  # 命名空间,用于环境隔离
        group: DEFAULT_GROUP  # 分组
        cluster-name: BEIJING  # 集群名称
        weight: 1  # 权重,用于负载均衡
        metadata:
          version: 1.0.0
          environment: production
        
        # 注册配置
        enabled: true  # 是否开启服务注册
        register-enabled: true  # 是否注册实例
        
        # 发现配置
        fail-fast: true  # 是否快速失败
        naming-load-cache-at-start: true  # 启动时加载缓存

// 3. 启动类配置
@SpringBootApplication
@EnableDiscoveryClient
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}

五、高可用服务发现架构设计

5.1 多注册中心架构

多注册中心高可用架构
┌─────────────────────────────────────────────────┐
│               多注册中心集群                      │
│  ┌─────────────┐       ┌─────────────┐         │
│  │  Nacos集群   │       │  Eureka集群  │         │
│  │  北京机房    │       │  上海机房    │         │
│  └──────┬──────┘       └──────┬──────┘         │
│         │                     │                │
│         └─────────┬───────────┘                │
│                   │ 注册中心间数据同步           │
└───────────────────┼─────────────────────────────┘
                    │
          ┌─────────▼─────────┐
          │   服务实例集群      │
          │  ┌───┐  ┌───┐    │
          │  │ A │  │ B │    │
          │  │实例│  │实例│    │
          │  └───┘  └───┘    │
          │  ┌───┐  ┌───┐    │
          │  │ C │  │ D │    │
          │  │实例│  │实例│    │
          │  └───┘  └───┘    │
          └──────────────────┘

5.2 客户端容错策略

// 客户端容错配置示例
@Configuration
public class DiscoveryConfig {
    
    @Bean
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
        return ServiceInstanceListSupplier.builder()
                .withDiscoveryClient()
                .withHealthChecks()  // 健康检查
                .withCaching()  // 缓存
                .withRetryAwareness()  // 重试感知
                .build(context);
    }
    
    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {
        return WebClient.builder()
                .filter(new LoadBalancerClientFilter(loadBalancerClientFactory()));
    }
}

// 服务调用容错配置
@Configuration
public class ResilienceConfig {
    
    @Bean
    public Customizer<Resilience4JCircuitBreakerFactory> defaultCustomizer() {
        return factory -> factory.configureDefault(id -> new Resilience4JConfigBuilder(id)
                .timeLimiterConfig(TimeLimiterConfig.custom()
                        .timeoutDuration(Duration.ofSeconds(5))
                        .build())
                .circuitBreakerConfig(CircuitBreakerConfig.custom()
                        .slidingWindowSize(10)
                        .failureRateThreshold(50)
                        .waitDurationInOpenState(Duration.ofSeconds(10))
                        .build())
                .build());
    }
}

思考与练习

  1. 设计一个三节点的Eureka集群,并配置服务间的数据同步。
  2. 实现一个基于Nacos的服务发现系统,支持多环境隔离和权重配置。
  3. 分析在注册中心宕机的情况下,服务调用应该如何保证可用性?
  4. 设计一个服务注册与发现的监控告警系统,监控关键指标。
关键要点总结:
registry-fetch-interval-seconds: 5 instance: # 使用IP地址注册 prefer-ip-address: true # 实例ID格式 instance-id: ${spring.application.name}:${server.port} # 心跳间隔(秒) lease-renewal-interval-in-seconds: 5 # 过期时间(秒) lease-expiration-duration-in-seconds: 10

Consul 实战

Consul 是 HashiCorp 开源的服务网格解决方案,采用 CP 架构(一致性优先)。

3. Consul 安装与启动

# 下载并启动 Consul(开发模式)
consul agent -dev

# 访问 Web UI
http://localhost:8500

# 查看集群成员
consul members

# 查看服务列表
consul catalog services

4. Spring Boot 集成 Consul

// pom.xml 依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-consul-discovery</artifactId>
</dependency>

# application.yml 配置
spring:
  application:
    name: user-service
  cloud:
    consul:
      host: localhost
      port: 8500
      discovery:
        # 启用服务发现
        enabled: true
        # 服务名称
        service-name: ${spring.application.name}
        # 健康检查路径
        health-check-path: /actuator/health
        # 健康检查间隔
        health-check-interval: 10s
        # 实例ID
        instance-id: ${spring.application.name}:${server.port}
        # 使用IP注册
        prefer-ip-address: true

Nacos 实战

Nacos 是阿里巴巴开源的动态服务发现、配置管理平台,支持 AP 和 CP 两种模式。

5. Nacos Server 启动

# 下载 Nacos
wget https://github.com/alibaba/nacos/releases/download/2.2.0/nacos-server-2.2.0.tar.gz
tar -xvf nacos-server-2.2.0.tar.gz

# 启动 Nacos(单机模式)
cd nacos/bin
sh startup.sh -m standalone

# 访问控制台
http://localhost:8848/nacos
# 默认账号密码:nacos/nacos

6. Spring Boot 集成 Nacos

// pom.xml 依赖
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</version>2.2.9.RELEASE</version>
</dependency>

# application.yml 配置
spring:
  application:
    name: product-service
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
        # 命名空间ID(用于环境隔离)
        namespace: dev
        # 分组
        group: DEFAULT_GROUP
        # 集群名称
        cluster-name: DEFAULT
        # 元数据
        metadata:
          version: 1.0
          region: cn-hangzhou

7. 服务发现代码示例

@RestController
@RequestMapping("/api")
public class ServiceDiscoveryController {

    @Autowired
    private DiscoveryClient discoveryClient;

    // 获取所有服务列表
    @GetMapping("/services")
    public List<String> getServices() {
        return discoveryClient.getServices();
    }

    // 获取指定服务的实例列表
    @GetMapping("/instances/{serviceName}")
    public List<ServiceInstance> getInstances(@PathVariable String serviceName) {
        return discoveryClient.getInstances(serviceName);
    }

    // 调用其他服务
    @GetMapping("/call/{serviceName}")
    public String callService(@PathVariable String serviceName) {
        List<ServiceInstance> instances = discoveryClient.getInstances(serviceName);
        if (instances.isEmpty()) {
            return "服务不可用";
        }

        // 简单轮询负载均衡
        ServiceInstance instance = instances.get(0);
        String url = "http://" + instance.getHost() + ":" + instance.getPort() + "/api/info";

        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.getForObject(url, String.class);
    }
}

高可用集群部署

8. Eureka 集群配置

# Eureka Server 1 (application-peer1.yml)
server:
  port: 8761
eureka:
  instance:
    hostname: peer1
  client:
    service-url:
      defaultZone: http://peer2:8762/eureka/,http://peer3:8763/eureka/

# Eureka Server 2 (application-peer2.yml)
server:
  port: 8762
eureka:
  instance:
    hostname: peer2
  client:
    service-url:
      defaultZone: http://peer1:8761/eureka/,http://peer3:8763/eureka/

# 客户端配置多个注册中心
eureka:
  client:
    service-url:
      defaultZone: http://peer1:8761/eureka/,http://peer2:8762/eureka/,http://peer3:8763/eureka/

9. Nacos 集群配置

# cluster.conf 配置
192.168.1.101:8848
192.168.1.102:8848
192.168.1.103:8848

# application.properties 配置
spring.datasource.platform=mysql
db.num=1
db.url.0=jdbc:mysql://127.0.0.1:3306/nacos?characterEncoding=utf8
db.user.0=nacos
db.password.0=nacos

# 客户端配置
spring:
  cloud:
    nacos:
      discovery:
        server-addr: 192.168.1.101:8848,192.168.1.102:8848,192.168.1.103:8848
最佳实践:

实践练习

练习任务:
  1. 搭建 Eureka 集群:部署 3 个 Eureka Server 节点,实现高可用
  2. 创建服务提供者:开发一个用户服务(user-service),注册到 Eureka
  3. 创建服务消费者:开发一个订单服务(order-service),通过服务发现调用用户服务
  4. 测试故障转移:停止一个用户服务实例,观察服务发现的自动剔除机制
  5. 对比不同注册中心:分别使用 Eureka、Consul、Nacos 实现相同功能,对比差异
  6. 实现健康检查:自定义健康检查端点,模拟服务降级场景
  7. 元数据应用:使用元数据实现服务版本控制和灰度发布
  8. 性能测试:测试注册中心在高并发下的性能表现

常见问题

Q: Eureka 的自我保护模式是什么?

A: 当短时间内丢失大量心跳时,Eureka 会进入自我保护模式,不再剔除服务实例。这是为了防止网络分区导致的误判。生产环境建议开启,开发环境可以关闭。

Q: 如何选择 AP 还是 CP 模式?

A: AP(可用性优先)适合服务发现场景,允许短暂的数据不一致。CP(一致性优先)适合配置管理场景,要求数据强一致性。Nacos 支持两种模式切换。

总结

服务注册与发现是微服务架构的基础设施,解决了服务间的动态通信问题。通过本课学习,你应该掌握: