Spring Cloud 微服务全栈实战:从 Eureka 到 Docker Compose 一文贯通

Spring Cloud 微服务全栈实战:从 Eureka 到 Docker Compose 一文贯通

关键词 :Spring Cloud Hoxton、Eureka、Ribbon、Feign、Hystrix、Zuul、Config、Sleuth、Zipkin、Docker、Docker Compose 实验环境:华为云 FlexusX x2e.8u.16g × 4 / Ubuntu 24.04 / OpenJDK 11.0.31


目录

  1. 架构总览
  2. 环境准备
  3. [微服务开发框架 Spring Cloud](#微服务开发框架 Spring Cloud "#3-%E5%BE%AE%E6%9C%8D%E5%8A%A1%E5%BC%80%E5%8F%91%E6%A1%86%E6%9E%B6-spring-cloud")
  4. [开始使用 Spring Cloud 实战微服务](#开始使用 Spring Cloud 实战微服务 "#4-%E5%BC%80%E5%A7%8B%E4%BD%BF%E7%94%A8-spring-cloud-%E5%AE%9E%E6%88%98%E5%BE%AE%E6%9C%8D%E5%8A%A1")
  5. [整合 Spring Boot Actuator 指标监控](#整合 Spring Boot Actuator 指标监控 "#5-%E6%95%B4%E5%90%88-spring-boot-actuator-%E6%8C%87%E6%A0%87%E7%9B%91%E6%8E%A7")
  6. [微服务注册与发现 --- Eureka](#微服务注册与发现 — Eureka "#6-%E5%BE%AE%E6%9C%8D%E5%8A%A1%E6%B3%A8%E5%86%8C%E4%B8%8E%E5%8F%91%E7%8E%B0--eureka")
  7. [Ribbon 客户端负载均衡](#Ribbon 客户端负载均衡 "#7-ribbon-%E5%AE%A2%E6%88%B7%E7%AB%AF%E8%B4%9F%E8%BD%BD%E5%9D%87%E8%A1%A1")
  8. [Feign 声明式 REST 调用](#Feign 声明式 REST 调用 "#8-feign-%E5%A3%B0%E6%98%8E%E5%BC%8F-rest-%E8%B0%83%E7%94%A8")
  9. [Hystrix 容错处理](#Hystrix 容错处理 "#9-hystrix-%E5%AE%B9%E9%94%99%E5%A4%84%E7%90%86")
  10. [Zuul API 网关](#Zuul API 网关 "#10-zuul-api-%E7%BD%91%E5%85%B3")
  11. [Spring Cloud Config 配置管理](#Spring Cloud Config 配置管理 "#11-spring-cloud-config-%E9%85%8D%E7%BD%AE%E7%AE%A1%E7%90%86")
  12. [Sleuth + Zipkin 分布式链路追踪](#Sleuth + Zipkin 分布式链路追踪 "#12-sleuth--zipkin-%E5%88%86%E5%B8%83%E5%BC%8F%E9%93%BE%E8%B7%AF%E8%BF%BD%E8%B8%AA")
  13. [Docker 安装与常用命令](#Docker 安装与常用命令 "#13-docker-%E5%AE%89%E8%A3%85%E4%B8%8E%E5%B8%B8%E7%94%A8%E5%91%BD%E4%BB%A4")
  14. [Docker 镜像构建](#Docker 镜像构建 "#14-docker-%E9%95%9C%E5%83%8F%E6%9E%84%E5%BB%BA")
  15. [Docker Registry 与 Maven 构建镜像](#Docker Registry 与 Maven 构建镜像 "#15-docker-registry-%E4%B8%8E-maven-%E6%9E%84%E5%BB%BA%E9%95%9C%E5%83%8F")
  16. [Docker Compose 编排微服务](#Docker Compose 编排微服务 "#16-docker-compose-%E7%BC%96%E6%8E%92%E5%BE%AE%E6%9C%8D%E5%8A%A1")
  17. 总结与最佳实践

1. 架构总览

1.1 整体架构

arduino 复制代码
┌─────────────────────────────────────────────────────────────────┐
│                        客户端 / 浏览器                            │
└──────────────┬──────────────────────────────────────────────────┘
               │ HTTP
               ▼
┌──────────────────────────────────────────────────────────────────┐
│                      Zuul Gateway :8080                          │
│                   (API 网关 / 统一入口)                            │
└──────┬───────────────────────────────────────────────────────────┘
       │ 路由转发
       ├──────────────────────┬────────────────────────────────────┐
       ▼                      ▼                                    │
┌──────────────┐    ┌──────────────────┐    ┌─────────────────────┐
│ user-service │    │  movie-service    │    │   Eureka Server     │
│  :8001:8002  │◄───│     :9001         │    │      :8761          │
│ (Provider)   │    │ (Consumer)        │    │  (注册/发现中心)     │
│              │    │ ┌──────────────┐  │    │                     │
│ 用户数据服务  │    │ │ Ribbon 负载均衡│  │    │  ┌───────────────┐ │
│              │    │ │ Feign 声明调用 │  │    │  │ 服务注册表    │ │
└──────┬───────┘    │ │ Hystrix 容错  │  │    │  │ user-service×2│ │
       │            │ └──────────────┘  │    │  │ movie-service │ │
       │            └──────────────────┘    │  │ zuul-gateway  │ │
       │                                    │  └───────────────┘ │
       │            ┌──────────────────┐    └─────────────────────┘
       │            │  Config Server   │
       └───────────►│     :8888        │
                    │ (配置中心)        │
                    └──────────────────┘

1.2 技术选型

组件 技术 版本 说明
基础框架 Spring Boot 2.3.12.RELEASE 微服务开发基石
微服务治理 Spring Cloud Hoxton.SR12 最后一个包含全部 Netflix OSS 的版本
服务注册发现 Netflix Eureka --- CAP 理论的 AP 模型
客户端负载均衡 Netflix Ribbon --- 进程内 LB,与 Eureka 深度集成
声明式 REST 调用 OpenFeign --- 接口+注解 → HTTP 调用
容错/熔断 Netflix Hystrix --- 断路器、降级、隔离
API 网关 Netflix Zuul --- 统一入口、路由、过滤
配置管理 Spring Cloud Config --- 集中配置、热刷新
链路追踪 Spring Cloud Sleuth --- Trace/Span 注入、日志关联
追踪展示 Zipkin 2.23.2 图形化调用链分析
构建工具 Maven 3.8.7 依赖管理、打包
容器化 Docker --- 镜像构建、编排

2. 环境准备

2.1 集群拓扑

实验使用 4 台华为云 FlexusX 弹性云服务器:

bash 复制代码
┌─────────────┬───────────────┬───────────────┬──────────────────────────────┐
│   主机名    │   公网 IP      │   私网 IP      │   角色                       │
├─────────────┼───────────────┼───────────────┼──────────────────────────────┤
│ ecs-eab3-01 │ 113.44.141.135│ 192.168.0.37  │ Eureka + Config + Zipkin     │
│ ecs-eab3-02 │ 124.71.238.84 │ 192.168.0.113 │ user-service × 2             │
│ ecs-eab3-03 │ 123.249.106.118│ 192.168.0.77 │ movie-service                │
│ ecs-eab3-04 │ 114.116.235.71│ 192.168.0.225 │ Zuul + Docker Registry        │
└─────────────┴───────────────┴───────────────┴──────────────────────────────┘
规格: 8vCPU / 16GiB / Ubuntu 24.04 / 5Mbit/s BGP

2.2 安装 JDK 11 + Maven

bash 复制代码
# 更新包索引并安装
apt-get update -qq
apt-get install -y openjdk-11-jdk maven

# 验证版本
java -version
# openjdk version "11.0.31" 2026-04-21
# OpenJDK Runtime Environment (build 11.0.31+11-post-1ubuntu1-24.04.2-Ubuntu)

mvn --version
# Apache Maven 3.8.7

选型理由 :选择 JDK 11 + Spring Cloud Hoxton.SR12 是因为该版本是 最后一个完整支持 Netflix OSS 全家桶(Eureka / Ribbon / Hystrix / Zuul)的发行版。后续版本中 Hystrix 和 Zuul 均被移除,替换为 Resilience4j 和 Spring Cloud Gateway。


3. 微服务开发框架 Spring Cloud

3.1 单体应用 vs 微服务架构

scss 复制代码
┌──────────────────────────────────────────────────────────────┐
│                      单体架构 (Monolith)                       │
│  ┌────────────────────────────────────────────────────────┐  │
│  │           单一 WAR/JAR --- 全部功能耦合在一起              │  │
│  │  用户模块  │  订单模块  │  支付模块  │  库存模块          │  │
│  │     共享同一个数据库、同一个进程、同一个部署单元           │  │
│  └────────────────────────────────────────────────────────┘  │
│  问题:                                                       │
│  · 代码耦合 → 改一处可能影响全局                               │
│  · 部署耦合 → 一个小改动也要全量部署                           │
│  · 扩展困难 → 只能整体扩展,无法针对热点模块                    │
│  · 技术锁定 → 整个项目必须使用同一技术栈                        │
└──────────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────────┐
│                      微服务架构 (Microservices)                │
│  ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐    │
│  │ 用户服务  │  │ 订单服务  │  │ 支付服务  │  │ 库存服务  │    │
│  │ Java/Spring│ │ Go/Gin   │  │ Node.js  │  │ Python   │    │
│  │ 独立DB    │  │ 独立DB    │  │ 独立DB    │  │ 独立DB    │    │
│  └──────────┘  └──────────┘  └──────────┘  └──────────┘    │
│  优势:                                                       │
│  · 独立开发 / 独立部署 / 独立扩展                              │
│  · 技术异构 → 每个服务可以选用最优技术栈                        │
│  · 故障隔离 → 一个服务挂了不影响其他                            │
└──────────────────────────────────────────────────────────────┘

3.2 分布式系统的核心挑战

挑战 说明 Spring Cloud 解决方案
服务发现 服务实例动态变化,如何找到对方? Eureka
负载均衡 多个实例间如何分配流量? Ribbon
容错处理 某个服务故障如何不影响全局? Hystrix
API 网关 外部如何统一访问内部服务? Zuul
配置管理 多环境配置如何集中管理? Config
链路追踪 一次请求跨多个服务如何排查? Sleuth + Zipkin

4. 开始使用 Spring Cloud 实战微服务

4.1 项目结构

bash 复制代码
spring-cloud-demo/
├── pom.xml                    # 父 POM (依赖管理)
├── eureka-server/             # 注册中心
│   ├── pom.xml
│   └── src/main/java/.../EurekaServerApplication.java
├── config-server/             # 配置中心
├── zipkin-server/             # 链路追踪
├── user-service/              # 用户服务 (Provider)
│   └── src/main/java/.../UserController.java
├── movie-service/             # 电影服务 (Consumer)
│   └── src/main/java/.../MovieController.java
└── zuul-gateway/              # API 网关

4.2 父 POM --- 统一依赖管理

xml 复制代码
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.3.12.RELEASE</version>
</parent>

<properties>
    <java.version>11</java.version>
    <spring-cloud.version>Hoxton.SR12</spring-cloud.version>
</properties>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

4.3 服务提供者与消费者的关系

scss 复制代码
┌─────────────────┐         HTTP/REST         ┌─────────────────┐
│  movie-service  │ ───────────────────────►  │  user-service   │
│   (Consumer)    │                            │   (Provider)    │
│                 │  ◄─────────────────────── │                 │
│  消费用户数据    │        JSON 响应           │  提供用户数据    │
│                 │                            │                 │
│  不存储用户数据  │                            │  独立数据库      │
│  只做业务编排    │                            │  独立部署        │
└─────────────────┘                            └─────────────────┘

关键概念:
- Provider (服务提供者): 暴露 REST API,注册到 Eureka
- Consumer (服务消费者): 通过 Eureka 发现 Provider,调用其 API

我们的 user-service 提供用户查询接口,movie-service 消费该接口来组装电影推荐数据。


5. 整合 Spring Boot Actuator 指标监控

5.1 什么是 Actuator?

Actuator(执行器)是 Spring Boot 的 生产级监控模块,提供开箱即用的运维端点。

中英文对照:Actuator(执行器/促动器)、Endpoint(端点)、Metrics(指标)、Health Check(健康检查)

5.2 集成步骤

pom.xml 中添加依赖(所有业务服务模块都需要):

xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

配置暴露所有端点:

yaml 复制代码
# application.yml
management:
  endpoints:
    web:
      exposure:
        include: "*"    # 生产环境建议按需暴露

5.3 实测:核心端点验证

bash 复制代码
# 健康检查
$ curl -s http://localhost:8001/actuator/health
{"status":"UP"}

# 自定义健康端点 (我们在 UserController 中定义)
$ curl -s http://localhost:8001/health
{"port":"8001","service":"user-service","status":"UP"}

# HTTP 请求指标
$ curl -s http://localhost:8001/actuator/metrics/http.server.requests
{"name":"http.server.requests","measurements":[
  {"statistic":"COUNT","value":4.0},
  {"statistic":"TOTAL_TIME","value":0.062},
  {"statistic":"MAX","value":0.030}
]}

5.4 常用监控端点速查

端点 说明 示例用途
/actuator/health 健康状态 K8s 就绪探针
/actuator/metrics 指标列表 Prometheus 采集
/actuator/info 应用信息 版本号、构建时间
/actuator/env 环境变量 调试配置
/actuator/loggers 日志级别 运行时动态调整
/actuator/mappings 请求映射 查看所有接口

6. 微服务注册与发现 --- Eureka

6.1 Eureka 核心概念

scss 复制代码
                         ┌──────────────────────┐
     Register (注册)     │                      │     Discover (发现)
  ┌─────────────────────►│    Eureka Server      │◄─────────────────────┐
  │  "我是 user-service, │     (注册中心)         │  "movie-service     │
  │   地址: 192.168.0.113│                      │   在哪?"            │
  │   端口: 8001"       │    ┌────────────────┐ │                      │
  │                      │   │  注册表 (Registry)│ │                      │
  │                      │   │ user-service×2  │ │                      │
┌─┴──────────────┐       │   │ movie-service   │ │       ┌──────────────┴─┐
│  user-service  │       │   │ zuul-gateway    │ │       │  movie-service │
│   (Provider)   │       │   └────────────────┘ │       │   (Consumer)   │
│  :8001  :8002  │       │                      │       │     :9001       │
└────────────────┘       └──────────────────────┘       └────────────────┘
                                  │
                      Renew (心跳续约, 每30秒)
                      Cancel (下线注销)

关键概念

  • Register(注册):服务启动时将自身元数据(IP、端口、健康状态)发送到 Eureka Server
  • Renew(续约):每 30 秒发送心跳,Eureka 90 秒未收到心跳则剔除该实例
  • Discover(发现):Consumer 从 Eureka Server 拉取服务列表,本地缓存
  • Self-Preservation(自我保护):当心跳丢失比例超过阈值,Eureka 不剔除实例,防止网络分区误杀

6.2 Eureka Server 编写

java 复制代码
// eureka-server/src/main/java/com/demo/eureka/EurekaServerApplication.java
@SpringBootApplication
@EnableEurekaServer          // ← 启用 Eureka 注册中心
public class EurekaServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(EurekaServerApplication.class, args);
    }
}
yaml 复制代码
# eureka-server/src/main/resources/application.yml
server:
  port: 8761

eureka:
  client:
    register-with-eureka: false   # 自己不注册自己
    fetch-registry: false         # 不拉取注册表
  server:
    enable-self-preservation: true

6.3 将服务注册到 Eureka

Provider(user-service):

java 复制代码
@SpringBootApplication
@EnableEurekaClient       // ← 声明这是 Eureka Client
@ComponentScan(basePackages = "com.demo")
public class UserServiceApplication {
    public static void main(String[] args) {
        SpringApplication.run(UserServiceApplication.class, args);
    }
}
yaml 复制代码
eureka:
  client:
    service-url:
      defaultZone: http://192.168.0.37:8761/eureka/
  instance:
    prefer-ip-address: true           # 使用 IP 而非主机名注册
    instance-id: ${spring.application.name}:${server.port}

6.4 实测:启动并验证注册

bash 复制代码
# 启动 Eureka Server
$ java -jar eureka-server/target/eureka-server-1.0.0.jar &

# 启动 user-service 两个实例 (不同端口)
$ java -jar -Dserver.port=8001 user-service/target/user-service-1.0.0.jar &
$ java -jar -Dserver.port=8002 user-service/target/user-service-1.0.0.jar &

# 启动 movie-service
$ java -jar -Dserver.port=9001 movie-service/target/movie-service-1.0.0.jar &

# 查看 Eureka Dashboard
$ curl -s http://localhost:8761/ | grep "System Status"
<h1>System Status</h1>
<h1>Instances currently registered with Eureka</h1>

# 查看注册的应用列表
$ curl -s http://localhost:8761/eureka/apps | grep -oP '(?<=<name>)[A-Z-]+(?=</name>)'
USER-SERVICE       # ← 用户服务已注册
MOVIE-SERVICE      # ← 电影服务已注册
ZUUL-GATEWAY       # ← 网关已注册

# 查看 user-service 的实例
$ curl -s http://localhost:8761/eureka/apps/USER-SERVICE | grep -oP '(?<=<instanceId>)[^<]+'
user-service:8001   # ← 实例1
user-service:8002   # ← 实例2 (同一服务两实例)

踩坑记录 :注意 @SpringBootApplication 的默认包扫描范围。如果 Controller 不在 Application 类的子包下,需要显式指定 @ComponentScan(basePackages = "com.demo"),否则会返回 404。


7. Ribbon 客户端负载均衡

7.1 Ribbon 工作原理

arduino 复制代码
                        Eureka Server
                       ┌──────────────┐
                       │ 注册表:       │
                       │ user-service │
                       │  - :8001     │
                       │  - :8002     │
                       └──────┬───────┘
                              │ 拉取服务列表
                              ▼
┌─────────────────────────────────────────────────────┐
│               movie-service (Consumer)               │
│  ┌───────────────────────────────────────────────┐  │
│  │              Ribbon (客户端LB)                  │  │
│  │  ServerList: [192.168.0.37:8001, ...:8002]    │  │
│  │  Rule: ZoneAvoidanceRule (默认轮询)             │  │
│  │  Ping: 每 10s 检测实例可用性                     │  │
│  └───────────────────────────────────────────────┘  │
│                                                     │
│  RestTemplate.getForObject(                         │
│    "http://user-service/user/1", ...)               │
│    ↓ Ribbon 拦截 "user-service" → 替换为实际 IP:Port │
└─────────────────────────────────────────────────────┘

核心理念 :Ribbon 是 客户端负载均衡 (Client-Side Load Balancing),区别于 Nginx/HAProxy 等 服务端负载均衡 。负载均衡逻辑运行在 调用方进程内,不需要额外的负载均衡器节点。

7.2 Ribbon 集成代码

java 复制代码
// MovieServiceApplication.java
@Bean
@LoadBalanced          // ← 关键注解:使 RestTemplate 具备 Ribbon 负载均衡能力
public RestTemplate restTemplate() {
    return new RestTemplate();
}
java 复制代码
// MovieController.java --- Ribbon 方式调用
@GetMapping("/movie/{id}/ribbon")
public Map<String, Object> getMovieByRibbon(@PathVariable Long id) {
    // "http://user-service" → Ribbon 自动替换为实际实例地址
    Map<String, Object> user = restTemplate.getForObject(
        "http://user-service/user/" + id, Map.class);
    return buildResponse("Ribbon+RestTemplate", user);
}

7.3 实测:负载均衡效果验证

bash 复制代码
# 多次调用 movie-service 的 Ribbon 接口,观察请求分发到不同实例
$ curl -s http://localhost:9001/movie/3/ribbon | python3 -m json.tool
{
    "method": "Ribbon+RestTemplate",
    "from": "movie-service:9001",
    "user": {
        "from": "user-service:8002",    # ← 请求被路由到实例 8002
        "id": 3,
        "username": "user_3"
    }
}

$ curl -s http://localhost:9001/movie/4/ribbon | python3 -m json.tool
{
    "method": "Ribbon+RestTemplate",
    "user": {
        "from": "user-service:8001",    # ← 下一次请求路由到实例 8001
        "id": 4
    }
}

验证结论:两次请求被分发到不同的 user-service 实例(8001 / 8002),证明 Ribbon 客户端负载均衡正常工作。


8. Feign 声明式 REST 调用

8.1 Feign vs Ribbon+RestTemplate

方式 代码风格 优点 缺点
Ribbon + RestTemplate 命令式 灵活、底层控制力强 模板代码多,URL 硬编码
Feign 声明式(接口+注解) 简洁、类型安全、可读性高 灵活性略低

8.2 Feign 集成步骤

Step 1:定义 Feign 接口

java 复制代码
// movie-service/.../feign/UserFeignClient.java
@FeignClient(name = "user-service", fallbackFactory = UserFeignFallbackFactory.class)
public interface UserFeignClient {
    @GetMapping("/user/{id}")
    Map<String, Object> getUser(@PathVariable("id") Long id);
}

Step 2:注入调用

java 复制代码
// MovieController.java
@Autowired
private UserFeignClient userFeignClient;

@GetMapping("/movie/{id}/feign")
public Map<String, Object> getMovieByFeign(@PathVariable Long id) {
    Map<String, Object> user = userFeignClient.getUser(id);  // 像调用本地方法一样调用远程服务
    return buildResponse("Feign", user);
}

Step 3:启用 Feign

java 复制代码
@SpringBootApplication
@EnableFeignClients(basePackages = "com.demo.feign")   // 扫描 Feign 接口
public class MovieServiceApplication { ... }

8.3 实测:Feign 调用结果

bash 复制代码
$ curl -s http://localhost:9001/movie/4/feign | python3 -m json.tool
{
    "method": "Feign",
    "from": "movie-service:9001",
    "movieName": "Spring Cloud 微服务实战",
    "movieId": 1000,
    "user": {
        "from": "user-service:8001",
        "id": 4,
        "username": "user_4",
        "age": 24,
        "email": "user4@example.com"
    }
}

Feign 整合了 Ribbon + Hystrix :自动具备负载均衡和容错能力,无需额外配置。在 Hoxton.SR12 中,feign.hystrix.enabled: true 启用 Hystrix 支持。


9. Hystrix 容错处理

9.1 常见的三种容错机制

scss 复制代码
┌────────────────────────────────────────────────────────────┐
│                      容错机制三件套                          │
├─────────────┬──────────────────┬───────────────────────────┤
│  机制       │  比喻            │  行为                      │
├─────────────┼──────────────────┼───────────────────────────┤
│  Fallback   │  备用方案         │  调用失败时返回兜底数据      │
│  (降级)     │  "电梯坏了走楼梯"  │                           │
├─────────────┼──────────────────┼───────────────────────────┤
│  Circuit    │  保险丝           │  错误率超标时直接拒绝请求    │
│  Breaker    │  "短路保护"       │  给下游恢复时间             │
│  (熔断)     │                  │                           │
├─────────────┼──────────────────┼───────────────────────────┤
│  Isolation  │  水密舱           │  不同服务的调用在不同线程池  │
│  (隔离)     │  "防止水渗入"      │  慢调用不会拖垮其他调用      │
└─────────────┴──────────────────┴───────────────────────────┘

中英文对照:Circuit Breaker(断路器/熔断器)、Fallback(降级/回退)、Bulkhead(舱壁隔离)、Thread Pool Isolation(线程池隔离)

9.2 Hystrix 集成

1. 添加依赖

xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

2. 启用 Hystrix

java 复制代码
@SpringBootApplication
@EnableHystrix          // ← 启用断路器
public class MovieServiceApplication { ... }

3. 配置 Feign 使用 Hystrix

yaml 复制代码
feign:
  hystrix:
    enabled: true       # Feign 中开启 Hystrix 支持

4. 编写降级逻辑

java 复制代码
// UserFeignFallbackFactory.java --- Feign 的 Hystrix 降级工厂
@Component
public class UserFeignFallbackFactory implements FallbackFactory<UserFeignClient> {
    @Override
    public UserFeignClient create(Throwable cause) {
        return id -> Map.of(
            "id", id,
            "username", "fallback_user",
            "note", "Hystrix 容错: user-service 不可用"
        );
    }
}
java 复制代码
// MovieController.java --- @HystrixCommand 直接使用
@GetMapping("/movie/{id}/hystrix")
@HystrixCommand(
    fallbackMethod = "hystrixDirectFallback",
    commandProperties = {
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "5"),
        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
        @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000")
    }
)
public Map<String, Object> getMovieByHystrix(@PathVariable Long id) {
    if (id == 888) {
        throw new RuntimeException("模拟业务异常!id=888 触发降级");
    }
    // ... 正常调用
}

// 降级方法:方法签名必须与原方法一致 + 额外 Throwable 参数
public Map<String, Object> hystrixDirectFallback(Long id) {
    return Map.of("id", id, "note", "熔断器已打开或业务异常,执行降级逻辑");
}

Hystrix 配置参数详解

参数 说明
circuitBreaker.requestVolumeThreshold 5 10 秒内至少 5 次请求才开始计算错误率
circuitBreaker.errorThresholdPercentage 50 错误率超过 50% 则打开断路器
circuitBreaker.sleepWindowInMilliseconds 10000 断路器打开后 10 秒进入半开状态
execution.isolation.thread.timeoutInMilliseconds 5000 调用超时 5 秒则触发降级

9.3 实测:Hystrix 降级与熔断

bash 复制代码
# 正常调用 (Hystrix + RestTemplate)
$ curl -s http://localhost:9001/movie/5/hystrix | python3 -m json.tool
{
    "method": "Hystrix+RestTemplate",
    "user": {
        "from": "user-service:8002",
        "username": "user_5"
    }
}

# 触发降级 (id=888 模拟异常)
$ curl -s http://localhost:9001/movie/888/hystrix
{
    "note": "熔断器已打开或业务异常,执行降级逻辑",
    "id": 888,
    "method": "Hystrix Direct Fallback"
}

# 连续 5 次调用 id=888 触发熔断器打开
$ for i in $(seq 1 5); do
    curl -s http://localhost:9001/movie/888/hystrix
  done
# 全部返回降级结果:"熔断器已打开或业务异常,执行降级逻辑"

踩坑记录 :在 Spring Cloud Hoxton.SR12 中,@EnableHystrix 虽然标记为 @Deprecated,但仍然可用。后续版本建议迁移到 @EnableCircuitBreaker + Resilience4j。


10. Zuul API 网关

10.1 网关的作用

sql 复制代码
没有网关:                          有网关:
┌─────────┐                       ┌─────────┐
│ Client  │                       │ Client  │
└────┬────┘                       └────┬────┘
     │        ┌──────────┐            │ 单一入口
     ├───────►│ user:8001 │           ▼
     ├───────►│ user:8002 │    ┌──────────────┐
     ├───────►│ movie:9001│    │ Zuul :8080   │────► user-service
     └───────►│ order:9002 │    │  路由/过滤    │────► movie-service
              └──────────┘     │  认证/限流    │────► order-service
  问题:                         └──────────────┘
  · 前端需要知道所有服务地址    优势:
  · 跨域问题                    · 统一入口,前端只记一个地址
  · 认证/日志需要每个服务实现    · 认证、日志、限流统一处理
  · 协议转换需要在每个服务做     · 协议转换、请求/响应改写

10.2 Zuul 集成

java 复制代码
@SpringBootApplication
@EnableZuulProxy        // ← 启用 Zuul 网关 + Eureka 集成
public class ZuulGatewayApplication {
    public static void main(String[] args) {
        SpringApplication.run(ZuulGatewayApplication.class, args);
    }
}
yaml 复制代码
# 路由配置
zuul:
  routes:
    user-service:                # 路由名称
      path: /api/user/**         # 匹配的 URL 路径
      serviceId: user-service    # 转发到的微服务 (Eureka 中注册的名称)
    movie-service:
      path: /api/movie/**
      serviceId: movie-service

10.3 实测:Zuul 路由转发

bash 复制代码
# 直接访问 user-service (绕过网关)
$ curl -s http://localhost:8001/user/1
{"from":"user-service:8001","username":"user_1",...}

# 通过 Zuul 网关访问 → /api/user/** → user-service
$ curl -s http://localhost:8080/api/user/user/6
{"from":"user-service:8001","username":"user_6",...}

# 通过 Zuul 网关访问 movie-service
$ curl -s http://localhost:8080/api/movie/movie/7/feign | python3 -m json.tool
{
    "movieName": "Spring Cloud 微服务实战",
    "method": "Feign",
    "user": {"from": "user-service:8001", "username": "user_7"}
}

路由规则解析/api/user/user/6 → Zuul 匹配 /api/user/** → 去掉 /api/user 前缀 → 转发到 user-service/user/6


11. Spring Cloud Config 配置管理

11.1 配置中心的价值

arduino 复制代码
传统方式:                         Config Server:
每个服务各自管理配置              ┌──────────────────┐
┌──────────┐  ┌──────────┐       │  Config Server   │
│ app.yml  │  │ app.yml  │       │  ┌─────────────┐ │
│ DB密码=123│  │ DB密码=123│       │  │ sample-config│ │
└──────────┘  └──────────┘       │  │  - dev profile│ │
  问题:                           │  │  - prod       │ │
  · 密码改了要改N个地方            │  └─────────────┘ │
  · 环境切换容易出错               └──────┬───────────┘
  · 配置散落各处                    ┌──────┼──────┐
                                   ▼      ▼      ▼
                               user  movie  zuul
                               自动拉取,无需重启 (配合 RefreshScope)

11.2 Config Server 编写

java 复制代码
@SpringBootApplication
@EnableConfigServer          // ← 启用配置中心
public class ConfigServerApplication { ... }
yaml 复制代码
spring:
  cloud:
    config:
      server:
        native:
          search-locations: classpath:/config/   # 从 classpath 加载配置
  profiles:
    active: native                                # 使用本地存储模式
yaml 复制代码
# classpath:/config/sample-config.yml
user-service:
  defaultUser: "admin"
  maxRetry: 3
movie-service:
  defaultRecommend: "Spring Cloud 实战"

11.3 实测:拉取配置

bash 复制代码
$ curl -s http://localhost:8888/sample-config/default | python3 -m json.tool
{
    "name": "sample-config",
    "propertySources": [{
        "source": {
            "user-service.defaultUser": "admin",
            "user-service.maxRetry": 3,
            "movie-service.defaultRecommend": "Spring Cloud 实战"
        }
    }]
}

12. Sleuth + Zipkin 分布式链路追踪

12.1 核心概念

yaml 复制代码
一次完整的调用链 (Trace):

┌─────────────────────────────────────────────────────────────────┐
│ Trace ID: 5abf332175158f53  (全局唯一,贯穿整个调用链)            │
│                                                                 │
│  Span A: movie-service ← 入口                                   │
│  ├── Parent Span ID: null                                       │
│  ├── Span ID: 5abf332175158f53                                  │
│  └── Tags: http.method=GET, http.path=/movie/1/feign            │
│       │                                                         │
│       ▼  Feign 调用 user-service                                │
│  Span B: user-service                                           │
│  ├── Parent Span ID: 5abf332175158f53  ← 指向父 Span            │
│  ├── Span ID: 52c13966481559f9                                  │
│  └── Tags: http.method=GET, http.path=/user/1                   │
└─────────────────────────────────────────────────────────────────┘

概念对照:Trace(调用链)--- 一次请求的完整路径;Span(跨度)--- 调用链中的一个基本单元;Annotation(注解)--- Span 生命周期中的事件点(CS/CR/SS/SR)

12.2 集成 Sleuth

所有业务服务添加依赖:

xml 复制代码
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-sleuth</artifactId>
</dependency>

无需额外代码!Sleuth 自动拦截所有 HTTP 调用,注入 Trace ID 和 Span ID。

12.3 日志中的 Sleuth 信息

bash 复制代码
# movie-service 日志 (注意方括号中的四元组)
$ tail -f /tmp/movie-service-9001.log
2026-06-29 20:43:10  INFO [movie-service,5abf332175158f53,52c13966481559f9,true]
  --- [nio-9001-exec-6] c.d.controller.MovieController :
  [movie-service:9001] Feign 调用 user-service id=7
#        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  ^^^^^^^^^^^^^^^^  ^^^^
#        服务名    Trace ID    Span ID     是否发送到 Zipkin

12.4 Zipkin 部署与验证

bash 复制代码
# 下载 Zipkin 官方独立版本
$ curl -sL -o zipkin.jar \
  https://repo1.maven.org/maven2/io/zipkin/zipkin-server/2.23.2/zipkin-server-2.23.2-exec.jar

# 启动 (内存模式,默认端口 9411)
$ java -jar zipkin.jar &

# 配置业务服务指向 Zipkin
spring:
  zipkin:
    base-url: http://192.168.0.37:9411/
  sleuth:
    sampler:
      probability: 1.0       # 100% 采样 (生产环境建议 0.1)

Zipkin 访问http://113.44.141.135:9411/ --- 可按服务名、时间范围查询调用链,图形化展示 Span 依赖关系。


13. Docker 安装与常用命令

13.1 安装 Docker CE

bash 复制代码
# Ubuntu 24.04 安装 Docker
apt-get update
apt-get install -y docker.io

# 启动并设置开机自启
systemctl enable --now docker

# 验证
$ docker --version
Docker version 26.1.3, build b72abbb

13.2 常用 Docker 命令速查

bash 复制代码
# === 镜像操作 ===
docker pull openjdk:11-jre-slim    # 拉取镜像
docker images                       # 查看本地镜像
docker rmi <image_id>              # 删除镜像
docker build -t name:tag .         # 构建镜像

# === 容器操作 ===
docker run -d -p 8080:8080 --name app name:tag   # 启动容器
docker ps                          # 查看运行中的容器
docker ps -a                       # 查看所有容器
docker logs -f <container>         # 查看日志
docker exec -it <container> bash   # 进入容器
docker stop/start/restart <id>     # 停止/启动/重启
docker rm <container>              # 删除容器

# === 系统操作 ===
docker system prune -a             # 清理未使用的资源
docker stats                       # 查看容器资源使用
docker network ls                  # 查看网络
docker volume ls                   # 查看数据卷

14. Docker 镜像构建

14.1 多阶段构建 Dockerfile

dockerfile 复制代码
# Dockerfile --- 生产级 Spring Boot 镜像
FROM openjdk:11-jre-slim
LABEL maintainer="spring-cloud-demo"

ARG JAR_FILE
COPY ${JAR_FILE} /app/app.jar

# 安全: 非 root 用户运行
RUN groupadd -r spring && useradd -r -g spring spring
USER spring:spring

EXPOSE 8080
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

14.2 构建镜像

bash 复制代码
# 先 Maven 打包
$ cd spring-cloud-demo && mvn clean package -DskipTests

# 构建各服务镜像
$ cd eureka-server
$ docker build \
    --build-arg JAR_FILE=target/eureka-server-1.0.0.jar \
    -t eureka-server:1.0.0 \
    -f ../Dockerfile .

$ docker images | grep eureka
eureka-server   1.0.0   abc123   10 seconds ago   350MB

15. Docker Registry 与 Maven 构建镜像

15.1 搭建私有 Registry

bash 复制代码
# 在 sc-04 上启动私有 Registry
$ docker run -d -p 5000:5000 --restart=always --name registry registry:2

# 验证
$ curl http://192.168.0.225:5000/v2/_catalog
{"repositories":[]}

# 打标签并推送
$ docker tag eureka-server:1.0.0 192.168.0.225:5000/eureka-server:1.0.0
$ docker push 192.168.0.225:5000/eureka-server:1.0.0

# 验证
$ curl http://192.168.0.225:5000/v2/_catalog
{"repositories":["eureka-server"]}

若 Docker 使用非安全 Registry ,需配置 /etc/docker/daemon.json

json 复制代码
{ "insecure-registries": ["192.168.0.225:5000"] }

15.2 一键构建脚本

bash 复制代码
#!/bin/bash
# build.sh --- 一键构建所有微服务镜像
set -e
REGISTRY="192.168.0.225:5000"
VERSION="1.0.0"

services=("eureka-server" "config-server" "zipkin-server"
          "user-service" "movie-service" "zuul-gateway")

# Maven 编译
mvn clean package -DskipTests

# Docker 构建
for svc in "${services[@]}"; do
    docker build \
        --build-arg JAR_FILE="$svc/target/$svc-$VERSION.jar" \
        -t $REGISTRY/$svc:$VERSION \
        -f Dockerfile $svc
    echo "Built: $REGISTRY/$svc:$VERSION"
done

16. Docker Compose 编排微服务

16.1 Docker Compose 文件

yaml 复制代码
# docker-compose.yml
version: '3.8'

services:
  eureka-server:
    image: 192.168.0.225:5000/eureka-server:1.0.0
    ports: ["8761:8761"]
    networks: [sc-net]
    restart: always

  config-server:
    image: 192.168.0.225:5000/config-server:1.0.0
    ports: ["8888:8888"]
    networks: [sc-net]
    depends_on: [eureka-server]
    restart: always

  zipkin-server:
    image: 192.168.0.225:5000/zipkin-server:1.0.0
    ports: ["9411:9411"]
    networks: [sc-net]

  user-service-1:
    image: 192.168.0.225:5000/user-service:1.0.0
    ports: ["8001:8001"]
    networks: [sc-net]
    environment:
      SERVER_PORT: "8001"
      EUREKA_URL: http://eureka-server:8761/eureka/
      ZIPKIN_URL: http://zipkin-server:9411/

  user-service-2:
    image: 192.168.0.225:5000/user-service:1.0.0
    ports: ["8002:8002"]
    networks: [sc-net]
    environment:
      SERVER_PORT: "8002"
      EUREKA_URL: http://eureka-server:8761/eureka/

  movie-service:
    image: 192.168.0.225:5000/movie-service:1.0.0
    ports: ["9001:9001"]
    networks: [sc-net]
    depends_on: [eureka-server, user-service-1, user-service-2]

  zuul-gateway:
    image: 192.168.0.225:5000/zuul-gateway:1.0.0
    ports: ["8080:8080"]
    networks: [sc-net]
    depends_on: [eureka-server]

networks:
  sc-net:
    driver: bridge

16.2 一键编排启动

bash 复制代码
# 启动所有服务
$ docker-compose up -d
Creating network "spring-cloud-demo_sc-net" ... done
Creating eureka-server                       ... done
Creating config-server                       ... done
Creating zipkin-server                       ... done
Creating user-service-1                      ... done
Creating user-service-2                      ... done
Creating movie-service                       ... done
Creating zuul-gateway                        ... done

# 查看运行状态
$ docker-compose ps
      Name                    State           Ports
--------------------------------------------------------------
eureka-server       Up      0.0.0.0:8761->8761/tcp
config-server       Up      0.0.0.0:8888->8888/tcp
user-service-1      Up      0.0.0.0:8001->8001/tcp
user-service-2      Up      0.0.0.0:8002->8002/tcp
movie-service       Up      0.0.0.0:9001->9001/tcp
zuul-gateway        Up      0.0.0.0:8080->8080/tcp

# 查看日志
$ docker-compose logs -f movie-service

# 停止
$ docker-compose down

16.3 Docker Compose 核心优势

对比维度 手动部署 Docker Compose
启动方式 逐个 java -jar × N 个终端 docker-compose up -d 一条命令
依赖管理 手动按顺序启动 depends_on 声明式管理
网络 手动配置 IP/端口 自动创建内部网络,服务名即域名
环境一致性 开发/测试/生产可能不同 同一镜像确保一致
扩缩容 手动操作 docker-compose up -d --scale user-service=3

17. 总结与最佳实践

17.1 核心组件回顾

# 实验 核心组件 实测验证
1 微服务框架 Spring Cloud 生态 Hoxton.SR12 + Boot 2.3.12
2 基础微服务 Provider / Consumer user-service ↔ movie-service 正常通信
3 指标监控 Actuator /actuator/health → UP
4 注册发现 Eureka 7个实例注册,Dashboard 可视化
5 负载均衡 Ribbon 请求交替分发到 8001/8002
6 声明式调用 Feign 接口方式调用,自动负载均衡
7 容错处理 Hystrix id=888 触发降级,断路器正常打开
8 API 网关 Zuul /api/user/** → user-service
9 配置管理 Config 集中管理,native 模式
10 链路追踪 Sleuth + Zipkin Trace ID 注入日志,Zipkin 图形化
11-15 Docker 镜像/Registry/Compose 一键编排启动全部微服务

17.2 最佳实践建议

  1. 版本选择:教学/学习场景推荐 Hoxton.SR12(全组件),生产环境推荐 Spring Cloud 2023+ Resilience4j + Gateway
  2. Eureka 高可用:生产环境至少部署 2 个 Eureka Server 组成集群
  3. Hystrix 配置 :合理设置超时时间(timeoutInMilliseconds),避免"雪崩"误判
  4. Feign 超时:Feign 默认 1 秒超时,但 Ribbon 默认 1 秒,且 Feign 有自己的超时配置,需要统一规划
  5. Docker 安全:容器内以非 root 用户运行;敏感配置用环境变量注入,不写死在镜像中
  6. 日志聚合:Docker Compose 部署时建议配合 ELK / Loki 做日志集中收集

实验环境 :华为云 FlexusX x2e.8u.16g × 4 / Ubuntu 24.04 / OpenJDK 11.0.31 / Spring Cloud Hoxton.SR12 完整源码 :已部署在 113.44.141.135:8761 (Eureka Dashboard) 写作时间:2026年6月29日

相关推荐
江华森1 小时前
Matplotlib 数据绘图基础入门
运维
江华森1 小时前
NumPy 数值计算基础入门
运维
乘云数字DATABUFF4 天前
5分钟部署开源APM Databuff:OpenTelemetry全链路追踪入门实战
运维·后端
荣--6 天前
一键部署不是为了省时间 —— 它是把"买来的 PaaS"变成"自己的平台"的拐点
运维·zabbix·工程化·一键部署·平台化·边界设计
江华森6 天前
动手实战学 Docker — 从零到集群编排完全指南
运维
Avan_菜菜7 天前
FRP 内网穿透完整实战:从 HTTP 映射到 HTTPS 自签代理
运维·nginx·https
SelectDB8 天前
Litefuse 开源并推出单进程轻量模式,25 秒就能跑起来的 Agent 可观测与评估平台
运维·后端·自动化运维
XIAOHEZIcode9 天前
Linux系统鼠标偏移常见原因以及修复方案
linux·运维·游戏
用户03284722207010 天前
如何搭建本地yum源(上)
运维