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

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

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

学习

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

更多

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

© 2026 CodeRoadMap

津ICP备2026012044号-1·津公网安备12011002026155号·coderoadmap@126.com
Spring Cloud 微服务入门/课时/微服务与 Spring Cloud
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综合练习与部署直觉

微服务与 Spring Cloud

单体拆分动机、Spring Cloud 在生态中的位置。

30 分钟

微服务与 Spring Cloud

这一课你将能做到

  1. 说清单体架构的三个典型痛点,以及「什么时候才值得拆微服务」的判断标准
  2. 讲清 Spring Boot 与 Spring Cloud 的分工:一个管单个应用怎么跑,一个管一群应用怎么协作
  3. 动手搭一个 Maven 多模块工程,让 user-service 和 problem-service 作为两个独立进程同时运行

为什么要学

你在 Spring Boot 实战入门 已经能把一个完整 API 跑起来——本站(CodeForge)就是这样一个单体:Spring Boot + MySQL + Redis + Nginx,用户、题目、提交、判题全在一个 jar 里。单体在中小规模完全够用,但它有三个天花板:

  1. 部署耦合:改一行判题逻辑,整包重新发布;判题把 CPU 打满,用户登录接口也跟着变慢(同一进程抢资源)。
  2. 扩容粗粒度:明明只有判题模块压力大,却只能把整个应用复制 3 份,白白多耗一倍内存。
  3. 团队互锁:A 组改公共 DTO 导致 B 组编译失败;全站被锁死在同一种数据库方案、同一个框架版本上。

微服务就是把这些业务能力(用户、题目、判题)拆成独立进程、独立数据库、独立部署的小服务。但拆开之后立刻冒出新问题:服务地址怎么找?请求从哪个口进来?配置改了怎么同步?下游挂了怎么办?Spring Cloud 就是解决这一整套「协作问题」的工具箱。

面试视角:「为什么用微服务」几乎是 Java 后端必考题,回答的关键不是背概念,而是结合具体场景讲痛点与收益——这一课给你这套判断力和话术。

先建立图像

单体 vs 微服务,用本站举例:

【单体:一个 jar 全包】
浏览器 ──► Nginx ──► codeforge.jar:8080
                      ├── UserController    ──► MySQL(共用一个库)
                      ├── ProblemController ──► 同一个库
                      └── JudgeController   ──► 同一个库 + Redis

【微服务:按业务拆进程】
浏览器 ──► Nginx ──► Gateway:8080
                      ├── user-service:8081    ──► user_db
                      ├── problem-service:8082 ──► problem_db
                      └── judge-service:8083   ──► judge_db + Redis
(每个服务独立启动、独立发布、独立扩容;注册中心记录各服务地址)

一句话直觉:单体是一家什么菜都做的餐厅,微服务是把后厨按菜系分成几个档口,各自采购(数据库)、各自加人(扩容),传菜口统一对外(网关)。

跟着做

步骤 1:建立 Maven 父工程

微服务通常一个服务一个 Maven 模块。新建目录 cloud-demo,写父 pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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>
    <groupId>com.example</groupId>
    <artifactId>cloud-demo</artifactId>
    <version>1.0.0</version>
    <packaging>pom</packaging>

    <modules>
        <module>user-service</module>
        <module>problem-service</module>
    </modules>

    <properties>
        <java.version>17</java.version>
        <spring-boot.version>3.2.5</spring-boot.version>
        <spring-cloud.version>2023.0.1</spring-cloud.version>
    </properties>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <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>
</project>

dependencyManagement 里的两个 BOM(依赖清单)统一管理全部子模块版本。注意:Spring Cloud 2023.0.x 必须搭配 Spring Boot 3.2.x/3.3.x,版本错配启动会报 NoSuchMethodError 之类莫名错误。

步骤 2:创建 user-service 模块

user-service/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>user-service</artifactId>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>
</project>

启动类 src/main/java/com/example/user/UserApplication.java:

package com.example.user;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

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

src/main/resources/application.yml:

server:
  port: 8081
spring:
  application:
    name: user-service

一个最简单的接口:

package com.example.user.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping("/users")
public class UserController {

    @GetMapping("/{id}")
    public Map<String, Object> get(@PathVariable Long id) {
        return Map.of("id", id, "nickname", "码农" + id, "from", "user-service");
    }
}

步骤 3:创建 problem-service 模块

结构与上面完全一样,只改三处:artifactId 为 problem-service、端口 8082、spring.application.name 为 problem-service。接口返回题目:

package com.example.problem.controller;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Map;

@RestController
@RequestMapping("/problems")
public class ProblemController {

    @GetMapping("/{id}")
    public Map<String, Object> get(@PathVariable Long id) {
        return Map.of("id", id, "title", "两数之和", "difficulty", "EASY");
    }
}

步骤 4:同时跑两个进程验证

在父目录开两个终端分别执行:

mvn spring-boot:run -pl user-service
mvn spring-boot:run -pl problem-service

分别访问 http://localhost:8081/users/1 和 http://localhost:8082/problems/1,各自返回数据,说明两个服务已作为独立进程运行。此刻它们还互不相识——下一课开始让它们「找到彼此」。

讲清楚要点

  1. 微服务按业务能力拆,不按技术层拆:拆「用户/题目/判题」是对的;拆「DAO 服务」「Service 服务」是错的——技术层拆完,每次改需求都要横跨所有服务。
  2. 一服务一库(理想状态):拆了进程仍共用一个 MySQL,等于只拆一半,表结构改动依然互锁。
  3. Spring Boot 管单个应用,Spring Cloud 管协作:Boot 解决「一个服务怎么起」,Cloud 解决「一群服务怎么找、怎么调、怎么稳」。
  4. Spring Cloud 不是一个大框架,而是一组 starter:注册发现、网关、配置、调用、熔断各是一个 starter,按需引入,用哪个学哪个。
  5. 拆分时机要克制:模块边界清晰 + 独立部署/扩容的真实诉求 + 团队规模上来,三者同时满足再拆;本站当前体量用单体是正确选择。

你可能会懵的地方

  • 微服务和 SOA 是一回事吗? SOA 是更早的「企业服务总线(ESB)」思路,靠中心化总线做协议转换;微服务去掉重总线,服务间轻量直连(HTTP/RPC),粒度更细、去中心化。面试一句「ESB 是 SOA 核心,微服务没有 ESB」即可。

  • 服务拆得越细越好吗? 不是。粒度过细会导致:一次业务操作跨 N 个服务、分布式事务变多、链路排错与运维成本爆炸。经验法则:先按业务边界拆到「一个团队维护 2-5 个服务」,够用就停。

  • Spring Cloud 官方组件和 Dubbo/Nacos/K8s 什么关系? 解决同一类问题的不同方案。Dubbo 是高性能 RPC 框架;Nacos 可同时替代 Eureka 和 Config;K8s Service 自带服务发现。原理相通,学会一套再看别的都是换名字。

  • 本站现在要不要拆? 不要。单机 Spring Boot + Redis + Nginx 在当前流量下性能充裕;先在代码里保持模块边界清晰(user/problem/judge 分包),等出现真实的独立扩容、独立发布诉求再拆,成本远低于过度设计。

  • 微服务一定要配 Docker/K8s 吗? 不强制,本地 java -jar 也能跑全套。但进程一多(10 个以上),手工管理启动顺序、环境变量会失控,容器编排几乎是标配,最后一课会讲部署直觉。

错法对照

错法问题正解
为简历好看把 3 人项目拆 12 个服务运维成本 > 收益,分布式问题全部暴露按 1-2 条业务线拆,能少则少
拆了进程仍共用一个数据库表结构互锁,伪微服务一服务一库,跨库数据用 API 聚合
Spring Cloud 版本随手填与 Boot 版本不兼容,启动报错用官方版本对照表,父 pom BOM 统一管理
一个模块直接 new 另一个模块的类破坏模块边界,无法独立部署通过 HTTP/RPC 接口通信,共享只放 DTO
在网关里写业务逻辑网关膨胀成新的单体网关只做路由/鉴权/限流,业务进服务

卡点急救

  1. 父工程导入后子模块依赖全红:BOM 没生效。检查子 pom 的 <parent> 是否指向父工程,先在父目录执行 mvn clean install -N 安装父 pom,再刷新子模块。
  2. 两个服务起一个挂一个:端口冲突。Windows 下 netstat -ano | findstr 8081 找占用进程;确认两个 yml 分别 8081/8082。
  3. 启动报 ClassNotFoundException 或方法不存在:Boot 与 Cloud 版本错配。Spring Boot 3.2.x 搭配 Spring Cloud 2023.0.x,不要混入 2.x 老 starter。

面试常问

  • 从单体到微服务,你的拆分依据是什么? 按业务能力(DDD 的限界上下文):用户、题目、判题各自的数据模型和变更节奏不同;再叠加非功能诉求——判题是 CPU 密集型,需要独立扩容和故障隔离。忌答「拆得细就是先进」。

  • 微服务带来哪些新问题?(考察有没有真做过) 分布式事务、链路排错难、数据一致性、网络不确定性(超时/重试/幂等)、运维复杂度。能说出 3 个以上并给出对应手段(熔断、trace-id、幂等键)就是加分回答。

  • Spring Cloud 和 Dubbo 怎么选? Cloud 是全家桶(网关/配置/熔断齐),生态偏 HTTP+REST;Dubbo 高性能 RPC + 服务治理,常配 Nacos/ZooKeeper。国内现状是 Spring Cloud Alibaba(Nacos+Sentinel+Seata)与原生 Cloud 并存,原理相通,答「按团队技术栈和性能诉求选」并给出理由即可。

自测三问

  1. 单体的三个典型痛点是什么?分别对应微服务的哪个收益?
  2. Spring Boot 与 Spring Cloud 各自解决什么层次的问题?
  3. 「按技术层拆服务」为什么是错的?

小结一句话

微服务是把系统按业务能力拆成独立进程与独立数据库的小服务,Spring Cloud 是让这群服务互相找到、稳定协作的工具箱。

延伸

  • Spring Cloud 官方项目页
  • 下一课:架构模式与术语——把注册中心、网关、熔断这些名词一次对齐。
加载本节练习…
下一课 →架构模式与术语