CodeRoadMap
首页路线题库资源库文章社区
CodeRoadMap

程序员的学习成长路线图。内容免费开放,登录后可同步路线图与课时进度。

由 程序员柏风 独立维护 · 关于作者

学习

  • 路线图
  • 课程
  • 面试题
  • 文章
  • 我

更多

  • 资源
  • 社区
  • 关于作者
  • 内容说明

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Spring Cloud 微服务入门/课时/服务注册与 Eureka
RawBlame
课时目录10 节

Spring Cloud 微服务入门

  1. 01微服务与 Spring Cloud
  2. 02架构模式与术语
  3. 03服务注册与 Eureka
  4. 04Spring Cloud Gateway
  5. 05Cloud Config 配置中心
  6. 06OpenFeign 服务调用
  7. 07熔断与容错
  8. 08Spring Data 扩展
  9. 09Micrometer 与可观测性
  10. 10综合练习与部署直觉

服务注册与 Eureka

注册中心、服务实例、心跳与健康。

35 分钟

服务注册与 Eureka

这一课你将能做到

  1. 独立搭起一个 Eureka 注册中心,并让 user-service、problem-service 注册上来,在控制台看到实例列表
  2. 说清注册、续约、剔除、自我保护四件事的触发条件与时间参数
  3. 把同一个服务起两个实例,体验「一个服务名、多个实例」的扩容模型

为什么要学

上一课留了尾巴:RestClient 里地址写死 http://localhost:8082。生产环境实例 IP 会变:扩容加机器、滚动发布换容器、宕机自动摘除。写死地址的服务一多,运维就崩溃了。

注册中心解决「服务在哪」:实例启动上报,调用方按名查询,宕机自动剔除。Eureka 是 Netflix 系经典实现,虽然新项目里 Nacos/Consul/K8s 更多,但它是理解一切注册中心的最短路径——注册、心跳、续约、剔除这套模型在 Nacos 里一模一样,面试也最爱从 Eureka 问起。

先建立图像

             ┌────────────────────────────────────────┐
             │          Eureka Server:8761            │
             │  服务注册表(内存 Map):                 │
             │  USER-SERVICE    → [10.0.0.5:8081, 10.0.0.6:8081]
             │  PROBLEM-SERVICE → [10.0.0.7:8082]
             └───▲──────────────▲──────────────▲──────┘
       注册+心跳30s│              │              │  拉取注册表(每30s)
             ┌────┴───┐   ┌──────┴───┐   ┌──────┴─────┐
             │ user:1 │   │  user:2  │   │  problem   │
             └────────┘   └──────────┘   └────────────┘

生命周期:启动注册 → 每 30s 心跳续约 → 90s 无心跳标记剔除 → 优雅下线主动注销

跟着做

步骤 1:搭建 Eureka Server

在父工程下新建模块 eureka-server,pom.xml:

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
         http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.example</groupId>
        <artifactId>cloud-demo</artifactId>
        <version>1.0.0</version>
    </parent>
    <artifactId>eureka-server</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
    </dependencies>
</project>

启动类:

package com.example.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@SpringBootApplication
@EnableEurekaServer
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}

application.yml:

server:
  port: 8761
spring:
  application:
    name: eureka-server
eureka:
  client:
    register-with-eureka: false   # 自己是注册中心,不注册自己
    fetch-registry: false         # 不拉取注册表
  server:
    enable-self-preservation: false  # 本地演示关掉自我保护,便于观察剔除

启动后访问 http://localhost:8761,能看到控制台首页(实例列表暂时为空)。

步骤 2:problem-service 注册上来

给 problem-service 的 pom 加依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>

application.yml:

server:
  port: 8082
spring:
  application:
    name: problem-service
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/
  instance:
    prefer-ip-address: true                    # 注册 IP 而非主机名,容器环境必配
    lease-renewal-interval-in-seconds: 30      # 心跳间隔
    lease-expiration-duration-in-seconds: 90   # 判死时间

spring.application.name 就是注册到 Eureka 的服务名(控制台转大写显示为 PROBLEM-SERVICE),别的服务以后按这个名字找它。Spring Cloud 2022+ 无需再写 @EnableDiscoveryClient,类路径上有 client 依赖即自动注册。

重启 problem-service,刷新 Eureka 控制台:PROBLEM-SERVICE 出现在 Application 表中,状态 UP (1)。

步骤 3:一服务多实例

再开一个终端,用命令行参数改端口再起一个实例:

mvn spring-boot:run -pl problem-service -Dspring-boot.run.arguments=--server.port=8083

控制台里 PROBLEM-SERVICE 变成 UP (2),点进去能看到 8082、8083 两个实例。调用方只认服务名,请求具体打给谁由客户端负载均衡决定——这就是水平扩容的全部秘密。

步骤 4:验证「自动发现」

在 user-service 中查注册表(演示用,生产走 Feign):

package com.example.user.controller;

import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
public class DiscoveryController {

    private final DiscoveryClient discoveryClient;

    public DiscoveryController(DiscoveryClient discoveryClient) {
        this.discoveryClient = discoveryClient;
    }

    @GetMapping("/discover/problem-service")
    public List<String> discover() {
        return discoveryClient.getInstances("problem-service").stream()
                .map(i -> i.getHost() + ":" + i.getPort())
                .toList();
    }
}

user-service 同样加上 eureka-client 依赖和 defaultZone 配置。访问 http://localhost:8081/discover/problem-service,返回两个实例地址——调用方不再需要知道任何 IP。

讲清楚要点

  1. 注册表在内存里:Eureka 用两级 ConcurrentHashMap 存注册表,读写极快;多节点间通过复制同步(Peer Awareness),优先保证可用性(AP 设计)。
  2. 心跳续约:客户端每 30s 发一次心跳证明「我活着」;90s 收不到就标记过期,由清理任务定期剔除。
  3. 自我保护模式:当「心跳失联比例超过阈值(默认 15%)」时,Eureka 宁可保留可能已死的数据也不剔除实例,防止网络抖动把健康服务团灭。生产建议开启。
  4. 缓存有延迟:Eureka 服务端有 readOnly/readWrite 两级响应缓存(30s),客户端本地也缓存注册表(30s),所以注册后最长约 30-60s 才能被其他服务感知,不是 bug。
  5. 优雅下线:kill -15 触发 shutdown 钩子主动注销,比等心跳超时快得多;K8s 里要配 preStop 才有等价效果。
  6. Eureka 2.x 已停更、1.x 维护模式:新项目国内多用 Nacos(注册+配置一体、带控制台),Cloud 原生栈可换 Consul 或 K8s Service DNS;核心模型一致。

你可能会懵的地方

  • 注册中心自己挂了怎么办? 客户端本地有全量注册表缓存,注册中心短暂宕机后服务间调用照常(按缓存地址直连),只是新实例无法注册。这就是 Eureka AP 设计的意义——注册中心不是单点故障。

  • Eureka 和 Nacos 怎么选? 一致性语义:Eureka 只有 AP;Nacos 支持临时实例(AP,心跳制)+ 持久实例(CP,Raft)两种模式,且自带配置中心和中文控制台。国内新项目偏 Nacos;学原理用 Eureka 足够,模型一致。

  • 为什么查到列表还要「负载均衡」? 注册中心只给地址列表(DiscoveryClient),选哪个实例是调用方(Spring Cloud LoadBalancer)的职责——两层分离,才能让 Feign、Gateway 各自定制策略。

  • prefer-ip-address 什么时候必须开? 容器/K8s 环境。默认注册主机名,跨机器解析不了;注册 IP+端口才能直连。本地多网卡环境若注册了虚拟网卡 IP,用 eureka.instance.ip-address 手工指定。

  • 服务下线了为什么还能被调到? 三层缓存叠加:Eureka 响应缓存 30s + 客户端缓存 30s + 负载均衡自身缓存。最多两分钟左右流量才彻底切走,设计重试与灰度发布时要预留这个窗口。

错法对照

错法问题正解
Server 忘配 register-with-eureka: false自己注册自己,控制台出现 EUREKA-SERVER单机关闭;集群模式下才开启互相注册
服务名带下划线 problem_service部分组件按主机名规则校验失败用中划线:problem-service
改完实例数立即验证调用方感知缓存未过期,请求仍打旧实例预留 30-60s 窗口,或调低缓存时间
生产环境关闭自我保护网络抖动时健康实例被团灭剔除保留自我保护,叠加主动健康检查
defaultZone 写死内网 IP 编进镜像环境迁移即失效环境变量 ${EUREKA_URL:...} 注入

卡点急救

  1. 控制台出现红色警告 EMERGENCY:自我保护触发(本地频繁重启导致心跳丢失比例超标)。演示环境加 enable-self-preservation: false,生产保留开启。
  2. 客户端启动报 Connection refused: localhost:8761:Server 没起,或 defaultZone 路径写错——正确路径必须以 /eureka/ 结尾。
  3. 注册成功但状态是 DOWN:健康检查未通过。加 spring-boot-starter-actuator 并配置 eureka.client.healthcheck.enabled: true,让 Eureka 用 /health 的结果而非心跳判断状态。

面试常问

  • 讲讲 Eureka 的自我保护机制。 Eureka 统计续约占比:若最近收到的续约数低于期望值的 85%,判定出现网络分区或本机网络异常,进入自我保护——不再剔除任何过期实例,注册表只增不减。目的是防止网络抖动团灭健康实例(AP 取舍);代价是可能保留死实例,需要客户端重试/熔断兜底。

  • Eureka、Nacos、Consul 对比? 从三个维度答:①CAP——Eureka 纯 AP;Nacos 临时实例 AP/持久实例 CP(Raft);Consul CP。②功能——Nacos 注册+配置一体带控制台;Consul 有强健康检查和 KV;Eureka 纯注册。③场景——K8s 体系常直接用 Service DNS 免注册中心。

  • 服务注册与发现的完整流程?(背到能复述) ①服务启动 → ②注册(上报实例信息)→ ③每 30s 续约心跳 → ④调用方每 30s 拉取注册表到本地缓存 → ⑤90s 无心跳剔除(触发自我保护判断)→ ⑥下线时主动注销。补一句「两层缓存导致 30-60s 感知延迟」是加分点。

自测三问

  1. 实例从注册到被剔除,完整生命周期经过哪几步?各自时间参数是多少?
  2. 自我保护模式为什么存在?开着有什么代价?
  3. 为什么服务注册成功后,调用方不会立刻发现它?

小结一句话

Eureka 是一张自动维护的电话簿:启动登记、心跳保活、超时销号,配合客户端缓存实现「按名字找服务」;理解它就理解了所有注册中心。

延伸

  • Spring Cloud Netflix 官方文档
  • 下一课:Spring Cloud Gateway——给微服务群装一扇统一的大门。
加载本节练习…
← 上一课架构模式与术语下一课 →Spring Cloud Gateway