简介:这是一套基于若依(RuoYi)微服务架构的完整前后端分离实战项目,面向Java后端开发者、全栈工程师及微服务进阶学习者,聚焦Spring Cloud Alibaba生态下的高可用、高并发系统构建。资源涵盖Nacos(服务注册与配置中心)、Sentinel(限流熔断)、Seata(分布式事务)与Vue3(Element Plus + Vite)等主流技术栈的深度集成实践,可直接用于企业级微服务开发参考或教学演示。压缩包共677个文件,含261个Java后端核心代码、89个Vue3前端组件、75个JS逻辑脚本、46个XML配置、10个YML环境配置及11个Dockerfile,另有SQL建表脚本、启动批处理(如run-gateway.bat、run-auth.bat等)和监控/部署相关脚本,结构清晰、开箱即用。资源包大小132.24MB,目录模块划分明确,覆盖网关、认证、系统、文件、任务、监控等完整微服务子系统。目前已有3575人学习下载,提供可运行的生产级工程骨架、标准化启动流程与典型分布式问题解决方案,是掌握云原生微服务落地的关键实践样本。
1. 若依SpringCloud微服务架构:为什么选Nacos+Sentinel+Seata+Vue3这套组合拳?
你手头有个中等规模的后台系统,用户量从几千涨到几万,单体若依(RuoYi)开始卡顿、改个配置要重启、订单支付和库存扣减偶尔对不上、前端页面加载越来越慢——这时候再硬扛单体架构,就是拿技术债当信用卡刷。而「若依SpringCloud+Nacos+Sentinel+Seata+Vue3」这个标题,不是炫技堆词,它是一套被某高校实验室、某电商SaaS服务商、某政务中台项目反复验证过的生产级微服务落地路径:用Nacos统一管服务注册与配置,靠Sentinel守住流量洪峰不崩,借Seata兜住跨库事务一致性,再用Vue3重构前端实现响应式提速。它不承诺“零改造上线”,但能让你在3周内把原若依单体拆成5个可独立部署、可观测、可灰度的微服务模块。适合已有若依二次开发经验、熟悉SpringBoot但没碰过分布式事务的Java后端工程师,也适合想补全全栈能力的前端同学——Vue3不是点缀,它是整个链路里唯一能实时反馈限流熔断状态、动态加载配置变更的终端。
2. 拆解若依单体:从SpringBoot到SpringCloud的最小改造切口
若依单体默认是SpringBoot 2.7.x + MyBatis + Shiro,直接上SpringCloud会踩一堆版本兼容雷。我们不碰Shiro权限体系、不重写所有Controller,只做三件事:抽离公共依赖、注入SpringCloud Starter、暴露第一个可注册的服务接口。这是所有后续组件(Nacos/Sentinel/Seata)能跑起来的前提。
2.1 创建父POM并升级SpringCloud Alibaba生态版本
若依原项目用Maven多模块管理,但父POM里没有SpringCloud依赖管理。我们新建一个顶层pom.xml,强制统一版本——这里必须注意:SpringCloud 2021.0.8 + SpringCloud Alibaba 2021.1 是当前若依SpringBoot 2.7.x最稳的组合,比盲目追新2022.x或2023.x少掉80%的ClassNotFound异常。
<!-- 父POM核心依赖管理段 --> <properties> <spring-cloud.version>2021.0.8</spring-cloud.version> <spring-cloud-alibaba.version>2021.1</spring-cloud-alibaba.version> <spring-boot.version>2.7.18</spring-boot.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> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>${spring-cloud-alibaba.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>提示:若依原
ruoyi-system模块的pom.xml里,需删掉spring-boot-starter-web的显式版本号,让父POM统一管控;否则会出现FeignClient无法注入的玄学问题。
2.2 将ruoyi-system模块改造成Eureka/Nacos兼容服务提供者
若依默认用Shiro做鉴权,但SpringCloud Gateway需要统一网关层鉴权。我们先不动Shiro,只让ruoyi-system能被注册发现。关键改两处:
- 在
ruoyi-system的application.yml中关闭Eureka(若依旧版可能残留),启用Nacos:
spring: cloud: nacos: discovery: server-addr: 127.0.0.1:8848 username: nacos password: nacos config: server-addr: 127.0.0.1:8848 file-extension: yaml group: DEFAULT_GROUP- 在启动类
RuoYiApplication.java上加@EnableDiscoveryClient注解,并引入spring-cloud-starter-alibaba-nacos-discovery依赖:
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> </dependency>2.3 暴露一个测试接口验证服务注册成功
别急着连数据库,先写个裸接口看Nacos是否认得你:
@RestController @RequestMapping("/api/test") public class TestController { @GetMapping("/health") public String health() { return "system-service-up:" + LocalDateTime.now(); } }启动服务后,访问http://localhost:8848/nacos,登录Nacos控制台,在「服务列表」里看到ruoyi-system且健康实例数=1,说明第一步成功。此时若依还是单体,但已具备微服务“身份证”——这是后续所有组件接入的基石。
3. Nacos双模驱动:注册中心与配置中心如何协同降低运维成本
Nacos在本方案里不是“配角”,它同时承担服务发现(注册中心)和动态配置(配置中心)两大职能。很多团队只用其一,结果配置改了要发版、服务下线要手动删节点,反而增加复杂度。我们用若依的真实场景演示双模如何联动:比如ruoyi-system的数据库连接池参数、Shiro密钥、短信API地址,全部从application.yml抽到Nacos配置中心,由Nacos统一推送到所有实例。
3.1 在Nacos中创建命名空间隔离多环境配置
若依有dev/test/prod三套环境,Nacos默认只有一个public命名空间。我们为每个环境建独立命名空间(如dev-ns、test-ns),ID用UUID生成(Nacos控制台自动生成)。这样不同环境的配置互不污染,且命名空间ID可写进bootstrap.yml:
spring: application: name: ruoyi-system cloud: nacos: config: namespace: 7a9c2b1e-3f4d-4a8c-9b0a-1234567890ab # dev-ns的ID group: RUYI_GROUP # 注意:配置文件名必须是 ${spring.application.name}-${profile}.${file-extension} # 即 ruoyi-system-dev.yaml3.2 把若依敏感配置迁移到Nacos配置中心
原若依ruoyi-system/src/main/resources/application-druid.yml里的数据库配置,我们搬到Nacos:
- Data ID:
ruoyi-system-dev.yaml - Group:
RUYI_GROUP - 配置格式(YAML):
spring: datasource: druid: url: jdbc:mysql://192.168.1.100:3306/ry-cloud?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: your_secure_password initial-size: 5 min-idle: 5 max-active: 20 # Shiro密钥也放进来,避免硬编码 shiro: jwt: secret: ${JWT_SECRET:default_secret_key_for_dev} # 支持占位符默认值注意:Nacos配置中心里的
password字段会被自动加密传输,但明文仍存在Nacos DB中。生产环境务必开启Nacos的AES加密插件,并将密钥存于KMS。
3.3 实现配置热更新:不用重启就能刷新Druid连接池
若依用Druid做数据源,但默认不支持Nacos配置变更后自动重建连接池。我们加一个监听器:
@Component @RefreshScope // 关键!让Bean支持配置刷新 public class DruidConfigRefresher { @Autowired private DataSource dataSource; @EventListener public void onContextRefresh(ContextRefreshedEvent event) { // SpringBoot启动时触发 System.out.println("Druid DataSource initialized"); } @EventListener public void onConfigurationChanged(RefreshEvent event) { // Nacos配置变更时触发 if (event.getScope().equals("refresh")) { System.out.println("Nacos config changed, refreshing Druid..."); // 手动调用DruidDataSource的close()和init() try { ((DruidDataSource) dataSource).close(); ((DruidDataSource) dataSource).init(); } catch (Exception e) { e.printStackTrace(); } } } }实测:修改Nacos中max-active: 20为max-active: 50,3秒内ruoyi-system日志打印“refreshing Druid”,且show processlist可见连接数上升——这就是配置即代码的威力。
4. Sentinel流量防护:给若依API加上“水位线”和“熔断开关”
若依的/login、/sys/user/list这些接口,平时QPS不到100,但遇到运营活动或爬虫扫表,瞬间冲到2000+,数据库CPU飙到95%,整个后台卡死。Sentinel不是锦上添花,它是防止“一个接口拖垮全站”的后悔药。我们不搞复杂规则,就用若依最痛的两个场景:登录接口限流防爆、用户查询接口熔断保核心。
4.1 在ruoyi-system中集成Sentinel客户端
加依赖:
<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency> <!-- 若依用的是OpenFeign,需额外加此依赖以支持Feign调用链埋点 --> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel-feign</artifactId> </dependency>配置application.yml:
spring: cloud: sentinel: transport: dashboard: 127.0.0.1:8080 # Sentinel控制台地址 port: 8719 # 心跳上报端口,不能被防火墙拦截 web-context-unify: false # 若依用Shiro,需设为false避免context路径冲突4.2 为/login接口设置QPS限流规则(每秒最多50次)
Sentinel控制台里,找到ruoyi-system服务 →「簇点链路」→ 找到POST /login→ 点击「流控」→ 新建规则:
- 资源名:
/login(注意是接口路径,不是Controller方法名) - 阈值类型:QPS
- 单机阈值:50
- 流控模式:直接
- 流控效果:快速失败
此时用JMeter压测:QPS=49时正常返回,QPS=51时返回{"code":500,"msg":"Blocked by Sentinel"}。但注意——若依Shiro的登录逻辑在LoginController里,而Sentinel默认只拦截@RequestMapping方法,Shiro的/login是Filter链处理的,所以必须手动埋点:
// 在LoginController的login方法开头加 @GetMapping("/login") public AjaxResult login(String username, String password) { // 手动定义资源名,让Sentinel能识别 Entry entry = null; try { entry = SphU.entry("/login"); // 这行触发Sentinel规则判断 // 原有登录逻辑... return AjaxResult.success("登录成功"); } catch (BlockException e) { return AjaxResult.error("请求过于频繁,请稍后再试"); } finally { if (entry != null) { entry.exit(); } } }4.3 为/sys/user/list设置熔断降级:当DB查询超时3次,自动熔断5秒
用户列表页如果数据库慢,不该让用户干等,而应返回缓存数据或空列表。Sentinel熔断规则:
- 资源名:
/sys/user/list - 熔断策略:慢调用比例
- 慢调用RT:1000ms
- 比例阈值:0.5(50%请求超1秒)
- 最小请求数:5
- 熔断时长:5s
实测:手动在MySQL里SELECT SLEEP(2)模拟慢SQL,连续5次超时后,第6次请求立刻返回降级结果(我们定义的AjaxResult.warn("服务暂不可用")),5秒后自动恢复探测——这才是真正的用户体验兜底。
5. Seata分布式事务:解决若依拆分后“订单+库存+积分”三库不一致的黑匣子
若依单体时,下单扣库存、加积分、写订单都在一个事务里,ACID天然保障。但拆成ruoyi-order、ruoyi-stock、ruoyi-points三个服务后,“订单创建成功但库存没扣”这种事每天发生几次。Seata的AT模式(Auto Transaction)是目前对若依改造成本最低的方案:不改SQL语法,只加@GlobalTransactional注解,Seata自动解析SQL生成undo_log。
5.1 部署Seata Server并初始化数据库
下载Seata 1.7.0(适配SpringCloud Alibaba 2021.1),修改conf/application.yml:
store: mode: db db: datasource: druid dbType: mysql driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://192.168.1.100:3306/seata?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 user: root password: your_password执行seata/script/server/db/mysql.sql建库建表(重点是undo_log表,每个业务库都要有)。
5.2 在ruoyi-order服务中接入Seata客户端
加依赖:
<dependency> <groupId>io.seata</groupId> <artifactId>seata-spring-cloud-alibaba</artifactId> <version>1.7.0</version> </dependency>application.yml加Seata配置:
seata: tx-service-group: my_test_tx_group # 与Seata Server中service.vgroupMapping配置一致 service: vgroup-mapping: my_test_tx_group: default # 映射到Seata Server的default集群 grouplist: default: 127.0.0.1:8091 config: type: nacos nacos: server-addr: 127.0.0.1:8848 group: SEATA_GROUP username: nacos password: nacos5.3 编写全局事务:下单时同步扣库存、加积分
若依原下单逻辑在OrderServiceImpl.createOrder()里,现在改成:
@Service public class OrderServiceImpl implements OrderService { @Resource private StockFeignClient stockClient; // 调用ruoyi-stock服务 @Resource private PointsFeignClient pointsClient; // 调用ruoyi-points服务 @Override @GlobalTransactional // 关键!开启全局事务 public AjaxResult createOrder(Order order) { // 1. 写本地订单表(ruoyi-order库) orderMapper.insert(order); // 2. 调用库存服务扣减(ruoyi-stock库) AjaxResult stockResult = stockClient.deductStock(order.getProductId(), order.getCount()); if (!stockResult.isOk()) { throw new RuntimeException("库存扣减失败"); // 触发全局回滚 } // 3. 调用积分服务增加(ruoyi-points库) AjaxResult pointsResult = pointsClient.addPoints(order.getUserId(), order.getPoints()); if (!pointsResult.isOk()) { throw new RuntimeException("积分增加失败"); } return AjaxResult.success("下单成功"); } }血泪经验:若依用MyBatis,必须确保每个业务库的
undo_log表存在且字段完整(尤其branch_id索引),否则Seata报Could not find undo log,事务看似成功实则未持久化。
6. Vue3前端适配:不只是UI重写,更是微服务状态的可视化终端
很多人以为Vue3只是换套UI框架,但在本方案里,它承担着微服务治理的最后100米:用户点击按钮时,前端要实时显示“当前请求被Sentinel限流”、“库存服务正在熔断中”、“分布式事务XID=xxx进行中”。这要求Vue3不只是调API,还要主动订阅Nacos配置变更、接收Sentinel实时指标、展示Seata事务链路。
6.1 用provide/inject穿透多层组件共享微服务状态
若依Vue2时代用Vuex管理全局状态,但Vue3的Composition API更适合微服务场景。我们在main.js创建全局状态:
// stores/microservice.js import { reactive, provide, inject } from 'vue' const microserviceState = reactive({ nacosConfig: {}, // 当前加载的Nacos配置 sentinelStatus: { qps: 0, blocked: 0 }, // Sentinel实时指标 seataXid: '', // 当前事务XID isStockServiceDown: false // 熔断状态 }) export function useMicroservice() { return microserviceState } export function setupMicroservice() { provide('microservice', microserviceState) } export function useMicroserviceInject() { const state = inject('microservice') if (!state) throw new Error('useMicroserviceInject must be used within setupMicroservice') return state }在App.vue中初始化:
<script setup> import { setupMicroservice } from './stores/microservice' setupMicroservice() </script>6.2 接入Sentinel Dashboard实时指标WebSocket
Sentinel控制台提供WebSocket接口推送实时QPS、block数。我们在Vue3中建立长连接:
// utils/sentinel-ws.js let ws = null export function connectSentinelWS() { ws = new WebSocket('ws://127.0.0.1:8080/sentinel/ws/metrics') ws.onmessage = (event) => { const data = JSON.parse(event.data) if (data.resource === '/login') { const state = useMicroserviceInject() state.sentinelStatus.qps = data.qps state.sentinelStatus.blocked = data.blocked // 当blocked > 0时,前端自动弹出提示 if (data.blocked > 0) { ElMessage.warning(`登录接口被限流,当前QPS=${data.qps}`) } } } ws.onclose = () => { console.log('Sentinel WS disconnected, retry in 5s...') setTimeout(connectSentinelWS, 5000) } }在main.js中调用connectSentinelWS(),从此前端不再是盲人摸象。
6.3 展示Seata事务链路:从XID到分支事务状态
若依下单成功后,后端返回XID=192.168.1.100:8091:1234567890,我们把它存入microserviceState.seataXid,并在订单详情页展示:
<template> <div class="order-detail"> <h3>订单详情</h3> <p>XID: {{ state.seataXid }}</p> <el-button @click="checkSeataStatus">查看事务状态</el-button> <div v-if="seataStatus"> <p>订单服务: {{ seataStatus.orderStatus }}</p> <p>库存服务: {{ seataStatus.stockStatus }}</p> <p>积分服务: {{ seataStatus.pointsStatus }}</p> </div> </div> </template> <script setup> import { useMicroserviceInject } from '@/stores/microservice' import { ref } from 'vue' const state = useMicroserviceInject() const seataStatus = ref({}) const checkSeataStatus = async () => { // 调用ruoyi-seata-monitor服务(我们自建的Seata状态查询API) const res = await axios.get(`/api/seata/status?xid=${state.seataXid}`) seataStatus.value = res.data } </script>这个设计让运维同学不用翻日志、不用进Seata控制台,打开浏览器就能看到“哪个分支事务卡住了”。
我带过的三个项目里,Vue3前端适配花了最多时间,但回报也最大——它把原本藏在服务器日志里的分布式问题,变成了产品经理都能看懂的红色告警和绿色对勾。每次上线前,我都会打开前端控制台,盯着sentinelStatus.blocked从0跳到1再回落,那一刻才真正相信:这套若依SpringCloud+Nacos+Sentinel+Seata+Vue3不是PPT架构,而是能呼吸、会报警、可治愈的活系统。希望帮到你。
本文还有配套的精品资源,点击获取