RuoYi-Cloud-Plus 微服务学习教程面向有 SpringBoot 基础的 Java 开发者,基于项目真实代码,从零掌握微服务架构标签:共 7 课 · 基于 v2.4.1 源码 · 含实操测试课程目录从单体到微服务的思维转变 — 理解为什么要拆服务服务注册与发现 Nacos — 服务之间怎么找到对方API 网关 Gateway — 统一入口、路由、鉴权、跨域服务间通信 Feign — 远程调用与降级兜底统一配置中心 — 配置集中管理与动态刷新分布式认证 Sa-Token — 微服务下登录状态共享公共模块设计与进阶 — 30+ 模块复用、多租户、缓存第一课:从单体到微服务的思维转变对应代码:pom.xml + 各服务 Application 启动类1.1 你熟悉的世界:SpringBoot 单体你熟悉的 SpringBoot 项目结构:// 单体应用:一个进程、一个端口、一个数据库 my-project/ src/main/java/com/example/ controller/ // 所有接口 service/ // 所有业务逻辑 mapper/ // 所有数据库操作 Application.java // 一个启动类 src/main/resources/ application.yml // 一个配置文件管所有 pom.xml // 一个 pom 管所有依赖方法之间直接调用,Controller → Service → Mapper,都在同一个 JVM 里。1.2 微服务的世界:拆成多个独立应用看这个项目的顶层pom.xml,有 7 个子模块:// 微服务:多个进程、多个端口、多个数据库 ruoyi-cloud-plus/ ├── ruoyi-gateway // 网关(端口 8080) ├── ruoyi-auth // 认证服务(端口 9210) ├── ruoyi-modules/ │ ├── ruoyi-system // 系统服务(端口 9201) │ ├── ruoyi-resource // 资源服务(端口 9204) │ ├── ruoyi-workflow // 工作流(端口 9205) │ ├── ruoyi-gen // 代码生成(端口 9202) │ └── ruoyi-job // 定时任务(端口 9203) ├── ruoyi-api // 服务间调用接口定义(不是独立进程) ├── ruoyi-common // 30+ 公共模块(不是独立进程) └── ruoyi-visual/ // 基础设施 ├── ruoyi-nacos // 注册中心(端口 8848) └── ...1.3 核心认知:每个微服务仍然是一个 SpringBoot 应用看RuoYiSystemApplication.java:@SpringBootApplicationpublicclassRuoYiSystemApplication{publicstaticvoidmain(String[]args){SpringApplicationapplication=newSpringApplication(RuoYiSystemApplication.class);application.run(args);}}跟单体应用的启动类一模一样。微服务不是推翻你学的东西,而是在 SpringBoot 基础上加了三样东西:你会的(单体)微服务新增的对应本项目代码Controller/Service/Mapper服务注册(告诉别人我存在)application.yml的nacos.discovery方法之间直接调用远程调用(Feign)ruoyi-api的@FeignClient一个 yml 管所有配置集中管理(Nacos)spring.config.import浏览器直接访问 Controller统一入口网关(Gateway)ruoyi-gateway模块1.4 一个微服务的配置长什么样看ruoyi-system/src/main/resources/application.yml:server:port:9201# 这个服务跑在 9201 端口spring:application:name:ruoyi-system# 服务名,别的服务靠这个名字找到它cloud:nacos:server-addr:127.0.0.1:8848# 注册中心地址discovery:group:DEFAULT_GROUP# 注册到哪个组config:import:# 从 Nacos 拉哪些配置文件-optional:nacos:application-common.yml-optional:nacos:datasource.yml-optional:nacos:ruoyi-system.yml本地 yml 只保留最基本的:我叫什么名字、我跑在哪个端口、我去哪里注册、我去哪里拉配置。真正的业务配置全在 Nacos。1.5 网关:前端的唯一入口前端请求 /system/user/list │ ▼ 网关 Gateway (:8080) │ 匹配路由规则 /system/** → ruoyi-system │ 去掉第一层前缀 → /user/list ▼ ruoyi-system 服务收到 /user/list所有前端请求先到网关,网关根据 URL 前缀决定转发给哪个服务。前端不需要知道后面有几个服务。[!note] 本课小结微服务= 把一个大应用拆成多个独立的小应用,每个独立部署、独立数据库每个微服务仍然是一个 SpringBoot 应用,启动类完全一样微服务比单体多了三样东西:服务注册、远程调用、统一配置前端只跟网关通信,网关负责路由到具体服务[!tip] 实操测试打开项目根目录pom.xml,找到modules标签,数一数有几个子模块分别打开RuoYiGatewayApplication.java和RuoYiSystemApplication.java,对比它们的启动类代码,确认都是@SpringBootApplication注意网关启动类多了一个exclude = {DataSourceAutoConfiguration.class},思考为什么网关不需要数据源(答案:网关只做路由转发,不直接操作数据库)打开ruoyi-system/src/main/resources/application.yml,找到spring.application.name和server.port,记住这个服务名和端口打开script/config/nacos/dynamic_routes.json,找到ruoyi-system-api路由配置,确认/system/**路径会转发到lb://ruoyi-system第二课:服务注册与发现 Nacos对应代码:application.yml 中 nacos.discovery 配置 + ServerNameConstants2.1 问题:服务之间怎么找到对方?在单体应用里,Service A 调用 Service B 就是直接注入然后调方法。但在微服务里,认证服务需要调用系统服务查用户信息,它们是两个独立的进程,跑在不同的端口上。你可能会想:直接写死 IP 和端口不就行了?但问题是:服务有多个实例(负载均衡),写死只能调一个服务重启后端口可能变不同环境(开发/测试/生产)IP 不同2.2 解决方案:注册中心(电话簿模式)Nacos 就是一个"服务电话簿"。每个服务启动时做两件事:服务启动 │ ├── 1. 注册:告诉 Nacos "我叫 ruoyi-system,跑在 192.168.1.10:9201" │ └── 2. 心跳:每隔几秒告诉 Nacos "我还活着" 服务调用方 │ └── 问 Nacos "ruoyi-system 在哪?" → Nacos 返回:192.168.1.10:9201, 192.168.1.11:9201 → 调用方选一个发起请求2.3 看真实代码:服务怎么注册每个微服务的application.yml都有这段配置:spring:cloud:nacos:server-addr:127.0.0.1:8848# Nacos 地址username:nacospassword:nacosdiscovery:group:DEFAULT_GROUP# 注册到哪个组namespace:${spring.profiles.active}# 命名空间,用环境名隔离[!note] 关键概念namespace(命名空间):用环境名隔离,dev 环境的服务只跟 dev 环境的互通,不会误连到 prod。group(分组):同一环境下进一步分组,一般用 DEFAULT_GROUP 即可。spring.application.name:服务名,这是注册到 Nacos 的唯一标识。别的服务就是靠这个名字找到你的。2.4 服务名常量:统一管理项目把所有服务名定义在ServerNameConstants.java中:publicinterfaceServerNameConstants{// ruoyi-system 服务名StringSYSTEM_SERVER_NAME="ruoyi-system";// ruoyi-resource 服务名StringRESOURCE_SERVER_NAME="ruoyi-resource";// ruoyi-workflow 服务名StringWORKFLOW_SERVER_NAME="ruoyi-workflow";}这个常量在 Feign 调用时用到——告诉 Feign “我要调用哪个服务”。2.5 负载均衡:多个实例怎么选当一个服务部署了多个实例(比如 ruoyi-system 部署了 3 台),Nacos 返回一个实例列表,调用方需要选一个。这个工作由Spring Cloud LoadBalancer完成。在网关的路由配置中,lb://ruoyi-system的lb://前缀就是"负载均衡"的意思——不是直接连某个 IP,而是先问 Nacos 拿到实例列表,然后选一个。[!warning] 注意本项目源码内置了 Nacos(ruoyi-visual/ruoyi-nacos),不是从 Docker 拉取的官方镜像。好处是可以断点调试 Nacos 本身,坏处是启动时需要先编译这个模块。[!note] 本课小结Nacos = 注册中心 + 配置中心,类似电话簿服务启动时自动注册到 Nacos,靠spring.application.name作为唯一标识调用方通过服务名从 Nacos 获取实例地址,不写死 IPlb://前缀表示负载均衡,由 Spring Cloud LoadBalancer 选实例namespace 做环境隔离,group 做分组管理[!tip] 实操测试打开ruoyi-common/ruoyi-common-core/src/main/java/.../constant/ServerNameConstants.java,确认三个服务名常量打开ruoyi-auth/src/main/resources/application.yml,找到spring.application.name,确认值为ruoyi-auth对比 auth 和 system 的 application.yml,发现它们连的 Nacos 地址完全一样,只是服务名不同打开script/docker/docker-compose.yml,找到nacos服务定义,确认端口映射8848:8848如果本地已启动 Nacos,浏览器打开http://localhost:8848/nacos(用户名/密码 nacos/nacos),在"服务管理 → 服务列表"中能看到注册的服务第三课:API 网关 Gateway对应代码:ruoyi-gateway 模块全部 + dynamic_routes.json3.1 为什么需要网关如果没有网关,前端需要知道每个服务的地址:没有网关(混乱)前端 → http://localhost:9210/auth/login 前端 → http://localhost:9201/system/user 前端 → http://localhost:9204/resource/upload 前端 → http://localhost:9205/workflow/start 问题: - 端口暴露 - 每个服务都要处理跨域 - 每个服务都要做鉴权 - 前端配置复杂有网关(整洁)前端 → http://localhost:8080/auth/login 前端 → http://localhost:8080/system/user 前端 → http://localhost:8080/resource/upload 前端 → http://localhost:8080/workflow/start 好处: - 统一端口 8080 - 统一跨域处理 - 统一鉴权 - 前端只配一个地址3.2 网关的核心功能:路由路由规则存储在 Nacos 的dynamic_routes.json中,由NacosRouteDefinitionRepository加载:// dynamic_routes.json 中的路由示例{"id":"ruoyi-system-api","uri":"lb://ruoyi-system",// 转发到 ruoyi-system 服务"predicates":[{"name":"Path","args":{"pattern":"/system/**",// 匹配 /system/ 开头的请求"pattern1":"/monitor/**"// 也匹配 /monitor/}}],"filters":[{"name":"CustomStripPrefix","args":{"parts":"1"}// 去掉第一层路径前缀}]}[!note] 路由工作流程请求/system/user/list→ 匹配/system/**→ 去掉前缀变成/user/list→ 转发到ruoyi-system服务的/user/list3.3 动态路由:修改不重启看NacosRouteDefinitionRepository.java的核心逻辑:// 启动时从 Nacos 拉取路由配置@OverridepublicFluxRouteDefinitiongetRouteDefinitions(){Stringcontent=nacosConfigManager.getConfigService().getConfig(dataId,dataGroup,5000);ListRouteDefinitionlist=JSONUtil.toList(content,RouteDefinition.class);returnFlux.fromIterable(list);}// 监听 Nacos 配置变更,自动刷新路由privatevoidaddListener(){nacosConfigManager.getConfigService().addListener(dataId,dataGroup,newListener(){@OverridepublicvoidreceiveConfigInfo(StringconfigInfo){// 配置变更 → 发布事件 → 网关自动重新加载路由SpringUtils.context().publishEvent(newRefreshRoutesEvent(this));}});}这意味着你在 Nacos 控制台修改dynamic_routes.json,网关会立即生效,不需要重启。3.4 网关过滤器:鉴权看AuthFilter.java,这是网关最核心的安全防线:@ConfigurationpublicclassAuthFilter{@BeanpublicSaReactorFiltergetSaReactorFilter(IgnoreWhitePropertiesignoreWhite){returnnewSaReactorFilter().addInclude("/**")// 拦截所有请求.addExclude("/favicon.ico","/actuator/**").setAuth(obj-{SaRouter.