一、核心概念

1.1 什么是服务注册与发现?

在微服务架构中,服务实例(如订单服务、用户服务)是动态的(可能因扩缩容、故障重启而变化)。服务注册与发现是解决服务间如何相互定位和通信的核心机制。

  • 服务注册 (Service Registration):服务启动时,将自己的网络地址(IP、端口)、服务名等信息注册到一个注册中心 (Registry)
  • 服务发现 (Service Discovery):服务需要调用其他服务时,向注册中心查询目标服务的可用实例列表,然后通过负载均衡选择一个实例进行调用。

1.2 核心组件

组件 说明
服务提供者 (Service Provider) 暴露服务的微服务实例(如 order-service)。启动时向注册中心注册自身信息。
服务消费者 (Service Consumer) 需要调用其他服务的微服务实例(如 user-service 调用 order-service)。从注册中心获取服务提供者列表。
注册中心 (Registry) 存储服务注册信息的中心化服务。Eureka 和 Nacos 都是注册中心的实现。
心跳机制 (Heartbeat) 服务提供者定期(如 Eureka 默认30秒)向注册中心发送心跳,证明自己“存活”。
服务下线 (Deregistration) 服务正常关闭时,主动通知注册中心注销自己。
服务剔除 (Eviction) 注册中心在一段时间内(如 Eureka 默认90秒)未收到服务提供者的心跳,则将其从注册列表中移除。

1.3 Eureka vs Nacos

特性 Eureka (Netflix) Nacos (Alibaba)
开发团队 Netflix Alibaba
功能定位 纯服务注册与发现 服务注册发现 + 配置管理 (一体化平台)
一致性协议 AP (高可用、分区容忍) 支持 AP (服务发现) 和 CP (配置管理)
健康检查 客户端心跳 支持心跳、TCP、HTTP、MySQL 等多种方式
集群模式 Peer-to-Peer (对等) 支持单机、集群(AP/CP)
配置中心 ❌ 需要配合 Spring Cloud Config ✅ 内置强大配置中心
控制台 简单 功能丰富(服务管理、配置管理、命名空间、权限等)
社区活跃度 Eureka 2.x 已停止开发,1.x 维护中 非常活跃,持续更新
推荐场景 现有 Eureka 项目 新项目,尤其需要配置中心

选择建议新项目强烈推荐 Nacos,功能更全面,社区更活跃。Eureka 可用于维护现有项目。


二、详细操作步骤

Part 1: 使用 Eureka 实现服务注册与发现

步骤 1.1:搭建 Eureka Server (注册中心)

  1. 创建项目: 使用 Spring Initializr 创建一个新项目,添加依赖:

    • Spring Web
    • Eureka Server (spring-cloud-starter-netflix-eureka-server)
  2. 配置 pom.xml

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
    </dependency>
    
  3. 启用 Eureka Server

    // EurekaServerApplication.java
    @SpringBootApplication
    @EnableEurekaServer // 启用 Eureka Server
    public class EurekaServerApplication {
        public static void main(String[] args) {
            SpringApplication.run(EurekaServerApplication.class, args);
        }
    }
    
  4. 配置 application.yml

    server:
      port: 8761 # Eureka Server 端口
    
    spring:
      application:
        name: eureka-server # 服务名
    
    eureka:
      client:
        register-with-eureka: false # 作为 Server,不向自己注册
        fetch-registry: false     # 作为 Server,不从自己拉取注册信息
      server:
        enable-self-preservation: true # 开启自我保护模式(网络分区时保护实例)
        eviction-interval-timer-in-ms: 30000 # 清理无效实例的间隔(默认60s)
      instance:
        hostname: localhost
    
  5. 启动 Eureka Server: 启动应用,访问 http://localhost:8761,应能看到 Eureka 控制台界面(此时无服务注册)。

步骤 1.2:服务提供者注册到 Eureka

  1. 创建服务提供者项目 (如 order-service): 添加依赖:

    • Spring Web
    • Eureka Client (spring-cloud-starter-netflix-eureka-client)
  2. 配置 pom.xml

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
    
  3. 配置 application.yml

    server:
      port: 8081 # 服务端口
    
    spring:
      application:
        name: order-service # 服务名,消费者通过此名发现
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka/ # 指向 Eureka Server 地址
      instance:
        prefer-ip-address: true # 注册时使用 IP 地址而不是主机名
        instance-id: ${spring.application.name}:${server.port} # 实例 ID 显示更清晰
    
  4. 编写一个简单接口

    @RestController
    @RequestMapping("/orders")
    public class OrderController {
        @GetMapping("/{id}")
        public String getOrder(@PathVariable String id) {
            return "Order: " + id + " from port " + server.port;
        }
    }
    
  5. 启动服务: 启动 order-service,刷新 Eureka 控制台,应能看到 ORDER-SERVICE 实例已注册。

步骤 1.3:服务消费者发现并调用服务

  1. 创建服务消费者项目 (如 user-service): 同样添加 Eureka Client 依赖。

  2. 配置 application.yml

    server:
      port: 8082
    
    spring:
      application:
        name: user-service
    
    eureka:
      client:
        service-url:
          defaultZone: http://localhost:8761/eureka/
      instance:
        prefer-ip-address: true
        instance-id: ${spring.application.name}:${server.port}
    
  3. 集成 Ribbon (负载均衡): Spring Cloud Netflix Ribbon 是客户端负载均衡器,通常与 Eureka 配合使用。

    • 注意:Ribbon 已进入维护模式,但仍在广泛使用。新项目可考虑 Spring Cloud LoadBalancer。
    • @Bean 配置 RestTemplate 时添加 @LoadBalanced 注解。
  4. 配置 RestTemplate

    // UserApplication.java 或配置类
    @Bean
    @LoadBalanced // 启用客户端负载均衡
    public RestTemplate restTemplate() {
        return new RestTemplate();
    }
    
  5. 编写调用逻辑

    @RestController
    @RequestMapping("/users")
    public class UserController {
    
        @Autowired
        private RestTemplate restTemplate;
    
        @GetMapping("/{userId}/orders/{orderId}")
        public String getUserOrder(@PathVariable String userId, @PathVariable String orderId) {
            // 使用服务名 "order-service" 进行调用,Ribbon 会自动解析并负载均衡
            String url = "http://order-service/orders/" + orderId;
            String response = restTemplate.getForObject(url, String.class);
            return "User: " + userId + ", " + response;
        }
    }
    
  6. 启动并测试: 启动 user-service,访问 http://localhost:8082/users/123/orders/456,应能成功调用 order-service 并返回结果。


Part 2: 使用 Nacos 实现服务注册与发现

步骤 2.1:部署 Nacos Server

  1. 下载 Nacos: 访问 Nacos GitHub Releases,下载 nacos-server-$version.zip

  2. 启动 Nacos Server

    • 单机模式 (开发测试):
      # Linux/Mac
      sh startup.sh -m standalone
      # Windows
      startup.cmd -m standalone
      
    • 集群模式 (生产):需配置 cluster.conf 和数据库。
  3. 访问 Nacos 控制台: 启动后,访问 http://localhost:8848/nacos,默认用户名/密码:nacos/nacos

步骤 2.2:服务提供者注册到 Nacos

  1. 创建服务提供者项目 (如 product-service): 添加依赖:

    • Spring Web
    • Nacos Discovery (spring-cloud-starter-alibaba-nacos-discovery)
  2. 配置 pom.xml

    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
  3. 配置 application.yml

    server:
      port: 8083
    
    spring:
      application:
        name: product-service
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848 # Nacos Server 地址
            # namespace: your-namespace-id # 指定命名空间 (生产环境隔离)
            # group: DEFAULT_GROUP # 指定分组
            # weight: 1 # 实例权重,用于负载均衡
            # metadata: # 自定义元数据
            #   version: 1.0.0
    
  4. 编写接口并启动

    @RestController
    @RequestMapping("/products")
    public class ProductController {
        @GetMapping("/{id}")
        public String getProduct(@PathVariable String id) {
            return "Product: " + id + " from port " + server.port;
        }
    }
    

    启动 product-service,登录 Nacos 控制台,在“服务管理” -> “服务列表”中应能看到 product-service

步骤 2.3:服务消费者发现并调用服务

  1. 创建服务消费者项目 (如 shopping-cart-service): 添加 Nacos Discovery 依赖。

  2. 配置 application.yml

    server:
      port: 8084
    
    spring:
      application:
        name: shopping-cart-service
      cloud:
        nacos:
          discovery:
            server-addr: localhost:8848
    
  3. 集成负载均衡

    • 推荐使用 Spring Cloud LoadBalancer (取代 Ribbon)。
    • 添加依赖:
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-loadbalancer</artifactId>
      </dependency>
      
    • 配置 RestTemplate
      @Bean
      @LoadBalanced
      public RestTemplate restTemplate() {
          return new RestTemplate();
      }
      
  4. 编写调用逻辑

    @RestController
    @RequestMapping("/cart")
    public class CartController {
    
        @Autowired
        private RestTemplate restTemplate;
    
        @GetMapping("/items/{productId}")
        public String getCartItem(@PathVariable String productId) {
            String url = "http://product-service/products/" + productId;
            String response = restTemplate.getForObject(url, String.class);
            return "Cart Item: " + response;
        }
    }
    
  5. 启动并测试: 启动 shopping-cart-service,访问 http://localhost:8084/cart/items/789,应能成功调用 product-service


三、常见错误与解决方案

错误现象 原因分析 解决方案
服务未注册到注册中心 1. 依赖缺失
2. 配置错误 (如 server-addr)
3. 网络不通
4. 启动类缺少 @EnableDiscoveryClient (通常不需要,自动配置)
1. 检查 pom.xml
2. 核对配置文件
3. pingtelnet 测试网络
4. 检查日志,看是否有注册相关错误
消费者调用失败 (404/UnknownHostException) 1. 服务名拼写错误
2. 服务未启动或未注册成功
3. 负载均衡配置问题 (@LoadBalanced)
1. 检查 RestTemplate URL 中的服务名
2. 确认提供者已注册
3. 确保 RestTemplate Bean 有 @LoadBalanced
Eureka Server 频繁进入自我保护模式 网络不稳定或心跳超时 1. 检查网络
2. 调整 eureka.instance.lease-renewal-interval-in-seconds (默认30s) 和 eureka.instance.lease-expiration-duration-in-seconds (默认90s)
3. 生产环境合理配置
Nacos 启动失败 (端口占用) 8848 或 9848 端口被占用 1. netstat -anp | grep 8848
2. 杀死占用进程或修改 application.properties 中的 server.port
Nacos 集群启动失败 cluster.conf 配置错误或数据库未初始化 1. 检查 cluster.conf 格式 (IP:PORT)
2. 执行 nacos/conf/nacos-mysql.sql 初始化数据库
3. 配置 application.properties 中的数据库连接

四、注意事项

  1. 服务命名
    • 使用小写字母、数字和连字符 -
    • 避免使用下划线 _
    • 名称应具有业务含义。
  2. 健康检查
    • Eureka:依赖客户端心跳。
    • Nacos:支持更丰富的健康检查方式,可在控制台配置。
  3. 元数据 (Metadata)
    • 可在注册时附加元数据(如版本号 version: 1.0),用于灰度发布、路由等高级场景。
  4. 命名空间 (Namespace)
    • Nacos 支持命名空间,用于环境隔离(如 dev, test, prod)。
  5. 分组 (Group)
    • Nacos 支持分组,默认 DEFAULT_GROUP,可用于逻辑分组。
  6. 版本兼容性
    • 确保 Spring Boot、Spring Cloud、Nacos/Eureka Client 版本兼容。参考官方文档的版本对应关系。
  7. 安全性
    • 生产环境务必开启 Nacos/Eureka 的认证(如 Nacos 的 nacos.core.auth.enabled=true)。

五、使用技巧

5.1 Nacos 配置中心集成 (Bonus)

Nacos 一大优势是同时作为配置中心。

# bootstrap.yml (优先级高于 application.yml)
spring:
  cloud:
    nacos:
      config:
        server-addr: localhost:8848
        file-extension: yaml # 配置文件格式
        # namespace: your-namespace-id
        # group: DEFAULT_GROUP

在 Nacos 控制台创建配置 Data ID = ${spring.application.name}.${file-extension} (如 product-service.yaml),即可动态管理配置。

5.2 服务健康检查自定义 (Nacos)

application.yml 中配置:

spring:
  cloud:
    nacos:
      discovery:
        health-check-type: http # 可选: tcp, http, mysql
        health-check-path: /actuator/health # HTTP 健康检查路径
        health-check-interval: 5s # 检查间隔

5.3 利用 Nacos 控制台

  • 服务详情:查看实例列表、健康状态、元数据。
  • 服务编辑:动态修改权重、元数据。
  • 订阅者:查看哪些服务订阅了本服务。
  • 配置管理:管理配置文件。

5.4 优雅下线

  • 正常关闭:服务收到 SIGTERM 信号时,会先向注册中心发送注销请求,再停止。
  • 强制关闭 (kill -9):依赖注册中心的剔除机制。

六、最佳实践

  1. 选择 Nacos:新项目首选 Nacos,功能更全面。
  2. 环境隔离:使用 Nacos 命名空间或 Eureka 不同的 defaultZone 隔离环境。
  3. 配置合理的超时
    • 服务调用超时 (feign.client.config.default.connect-timeout, read-timeout)。
    • 注册中心连接超时。
  4. 启用认证:生产环境必须开启注册中心的访问控制。
  5. 监控
    • 监控注册中心状态(CPU、内存、连接数)。
    • 监控服务实例的健康状态和数量。
  6. 服务治理
    • 结合 Sentinel (流量控制) 或 Hystrix (熔断) 提高系统韧性。
    • 利用 Nacos 的权重进行灰度发布。
  7. 文档化:明确服务名、接口、依赖关系。

七、性能优化

  1. 注册中心集群
    • 生产环境必须部署集群,避免单点故障。
    • Eureka:多节点 Peer-to-Peer 复制。
    • Nacos:集群模式,推荐持久化到数据库。
  2. 调整心跳与剔除间隔
    • 平衡:缩短间隔能更快发现故障,但增加网络和注册中心压力。
    • 建议:根据业务容忍度调整。例如,Eureka 可设 lease-renewal-interval-in-seconds=10, lease-expiration-duration-in-seconds=30
  3. 客户端缓存
    • Eureka/Nacos Client 会缓存服务列表,减少对注册中心的直接查询。
    • 避免在代码中频繁调用 DiscoveryClient.getInstances(),可结合缓存。
  4. 负载均衡优化
    • 使用更高效的负载均衡策略(如 Nacos 权重、一致性哈希)。
    • Spring Cloud LoadBalancer 性能优于旧版 Ribbon。
  5. 减少注册信息
    • 避免在元数据中存储大量数据。
  6. 网络优化
    • 确保服务与注册中心网络延迟低。

总结

服务注册与发现是微服务架构的基石。

  • Eureka:经典的 AP 模型注册中心,简单易用,但功能单一,社区趋于稳定。
  • Nacos:功能强大的国产平台,集服务发现与配置管理于一体,AP/CP 可切换,新项目首选

核心步骤

  1. 部署:启动 Eureka Server 或 Nacos Server。
  2. 注册:服务提供者添加 Client 依赖并配置 Server 地址。
  3. 发现:服务消费者添加 Client 依赖,配置 RestTemplate@LoadBalanced
  4. 调用:使用服务名(而非具体 IP:Port)通过 RestTemplate 调用。

掌握服务注册与发现,是构建可扩展、高可用微服务系统的起点。