尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Spring Boot + Vue微服务开源项目:从架构拆解到部署避坑全解析

Spring Boot + Vue微服务开源项目:从架构拆解到部署避坑全解析 如果你在 GitHub 上翻过 Spring Boot 相关的开源项目大概率会发现一个规律Spring Boot Vue 几乎是出现频率最高的组合如果再叠上“微服务架构”这个关键词那基本就是商城、管理系统、跨境电商这类项目的标配了。这三样东西放一起一方面说明这个技术栈非常成熟另一方面也意味着很多教程只是在“跑 demo”并没有把背后的设计逻辑讲透。实话说Spring Boot Vue 的微服务开源项目真正值得研究的东西不在代码量本身而在于“为什么这么拆”。为什么用户服务和订单服务要分开为什么前端要用 Vue Router 的动态路由而不是写死菜单为什么 Vue 打包之后要塞进 Spring Boot这些问题的答案才是你从“会跑 demo”到“能改造项目”的分水岭。这篇博文我不会给你一个“一键启动”的玩具代码而是以多商户跨境商城这类常见的开源项目为背景把 Spring Boot Vue 微服务项目的工程结构、服务拆分、路由设计、打包部署、监控运维整条链路拆开讲一遍。适合正在啃 GitHub 开源项目的读者、准备做课程设计或毕业设计的同学以及想从单体项目转向微服务架构的一线开发。1. 项目轮廓Spring Boot Vue 的微服务开源项目到底在解决什么问题1.1 为什么是 Spring Boot而不是直接上 Spring Cloud先说结论Spring Boot 是零件Spring Cloud 是装配车间。微服务架构里每一个独立的服务本质上都是一个可以独立启动的 Spring Boot 应用。Spring Cloud 负责把这些应用连起来解决服务发现、配置管理、网关路由这些“分布式”的问题。所以不存在“用 Spring Boot 还是用 Spring Cloud”的取舍而是先用 Spring Boot 把服务建起来再选择是否引入 Spring Cloud 生态。Spring Boot 最核心的价值是“约定优于配置”内嵌 Tomcat、自动配置、起步依赖让开发初期几乎不用写配置就能跑起来。对开源项目来说这意味着维护成本低、新手更容易上手。特别重要的一点是大量开源项目选择 Spring Boot 2.x而不是 3.x。这背后是版本兼容性问题Spring Boot 2.3.x 配合 Spring Cloud Hoxton2.6.x 配合 Spring Cloud 2021.0.x这套组合已经被无数生产项目验证过。Spring Boot 3 虽然性能更好但要求 JDK 17很多服务器环境还是 JDK 8/11所以开源项目的默认选择偏保守是有道理的。1.2 为什么 Vue 能成为前端默认选择Vue 能在这些开源项目里站稳脚跟靠的不是“比 React 好”而是“刚刚好”。它最大的特点是渐进式你可以在老项目里局部使用也可以像这些开源项目一样做成完整的前后端分离单页应用。Vue 的模板语法接近 HTML后端 Java 工程师转前端时学习曲线很低这一点对开源项目非常重要——维护者不需要找一个专门的前端就能改页面。再说生态Element Plus、Ant Design Vue 这些组件库把后台管理系统的表格、表单、弹窗都封装好了开发效率非常高。Vue Router 的动态路由能力配合后端返回的菜单权限数据可以做到不同角色登录后看到不同的侧边栏这正是商城管理后台的核心需求。Vue 3 配合 Vite 构建冷启动和热更新速度比老一代 Webpack 方案快太多日常开发体验很舒服。1.3 微服务架构不是银弹很多刚开始接触开源项目的人容易陷入一个误区看到“微服务”三个字就觉得高级自己的单体小项目也想硬拆。真实的微服务架构是为了解决团队协作和业务扩展问题的不是为了让技术栈好看。一个几十个接口的小系统拆成五六个微服务光启动顺序就能让你崩溃。开源项目之所以爱用微服务架构一般有两个原因。第一商城、电商这类业务天然适合按域拆分用户域、商品域、订单域、支付域每个域独立演进互不拖累尤其是多商户跨境商城不同商户的结算、权限、物流配置差异很大如果全塞在一个单体应用里代码耦合会膨胀到几乎没法维护。第二完整的微服务架构本身就是很好的学习素材读者可以从中看到服务注册、配置中心、网关、熔断限流这些真实生产组件是怎么配合的。提示如果你只是做毕业设计或者个人小项目单体 前后端分离就完全够用了。硬上微服务只会增加部署成本和学习成本最后很可能因为一个 Nacos 启动失败就卡住一整天。2. 开源项目里的微服务设计边界、组件与代码组织2.1 服务拆分原则与“对外接口放哪里”的答案服务拆分是微服务设计里最难的一步也是最容易扯皮的一步。我的经验是先按业务域画一张大图一个域对应一个服务然后把域之间的依赖关系列出来。比如多商户跨境商城典型的服务划分是这样的用户认证服务负责登录注册、Token 签发、商户服务负责商户入驻、资质审核、商品服务负责 SKU、库存、订单服务负责下单、状态流转、支付服务对接第三方跨境收款、网关服务统一入口。搜索词里有个很高的频率问题Spring Boot 对外提供的接口给第三方用的应该放在单独的服务里还是放在对应服务里我的回答很明确放在对应业务服务里不要单独为“对外接口”建一个空壳服务。原因很简单对外接口本质上还是要访问订单数据、商品数据你单独建一个接口服务它要么去调用其他服务的内部接口要么直接连别人的数据库这两种做法都会破坏微服务的边界。正确做法是在网关层按路径前缀区分比如/api/open/前缀的走开放接口/api/internal/前缀的只允许服务间调用。这样既保持了业务逻辑的内聚又能在网关层统一做鉴权、限流、日志。2.2 基础设施选型从 Eureka 到 Nacos从 Zuul 到 Gateway微服务架构除了业务服务本身还需要一批基础组件。我按开源项目里最常见的组合列了一个表也是我实际测试下来最稳的一套组件作用选择原因Nacos注册中心 配置中心取代 Eureka集服务发现和配置管理于一体Spring Cloud Gateway统一路由入口取代 Zuul基于 WebFlux吞吐量更好Sentinel熔断、限流、降级取代 Hystrix有控制台可以实时调整规则Spring Boot Admin服务监控轻量基于 Actuator页面直观OpenFeign服务间 HTTP 调用声明式客户端配合负载均衡使用为什么不用 Eureka因为 Spring Cloud 官方已经宣布 Eureka 进入维护模式而 Nacos 不仅能做注册中心还能做配置中心一个组件解决两个需求减少了部署组件数量。为什么网关用 Spring Cloud Gateway 不用 ZuulZuul 1.x 基于 Servlet是阻塞式模型在并发场景下容易成为瓶颈Gateway 基于 WebFlux 响应式模型性能更好。另外Gateway 可以很方便地和 Nacos、Sentinel 集成做动态路由和限流规则下发。2.3 开源项目的工程目录到底长什么样看一个开源项目先看目录结构比先看代码更高效。典型的 Spring Boot Vue 微服务项目根目录通常长这样mall-parent ├── mall-common # 公共模块统一返回体、工具类、异常处理 ├── mall-gateway # 网关服务路由转发、鉴权过滤 ├── mall-auth # 认证服务登录、令牌签发 ├── mall-user # 用户服务买家/卖家用户 ├── mall-product # 商品服务商品、分类、SKU、库存 ├── mall-order # 订单服务下单、状态机、售后 ├── mall-payment # 支付服务跨境收款渠道对接 ├── mall-admin # 后台管理服务运营后台聚合接口 ├── ui # Vue 前端工程 ├── docs # 部署文档、数据库脚本 └── docker-compose.yml # 一键编排方案前端不是塞进某个模块而是独立放在ui目录下这就是前后端分离的标准姿势。后端模块之间通过 OpenFeign 调用前端只跟网关打交道网关统一处理后转发到各个服务。有一点要留意很多开源项目会把数据库脚本放在docs里但实际生产里数据库建表脚本应该走迁移工具比如 Flyway 或 Liquibase而不是靠手工执行 SQL。看完目录结构你应该能大概判断一个项目的技术栈成熟度了。3. 实操把 Spring Boot Vue 的微服务商城项目跑起来3.1 后端工程初始化Maven 多模块怎么建我从零开始给你捋一遍这个过程。第一步是先建一个 Maven 父工程pom.xml里只放依赖管理和版本锁定不写任何业务代码。父工程的 packaging 必须是pom然后在这个基础上用 Spring Initializr 创建各个子模块。这里有个关键操作把版本号统一放在父工程的dependencyManagement里子模块里只写artifactId不写版本这样升级版本时只改一处。多模块工程建好之后最容易被忽视的是spring-boot-maven-plugin的配置。这个插件负责把服务打成可执行的 fat jar而对于mall-common这种公共模块不应该打包成可执行 jar需要在配置里显式声明跳过skiptrue/skip。否则你会遇到一个很诡异的问题编译成功了但启动时报“没有主清单属性”。3.2 Vue 环境安装与工程创建从零搭一个前端管理后台前端部分我以 Vue 3 Vite 为例。先确认 Node.js 版本Vite 4/5 要求 Node 16建议直接用 Node 18稳定且生态兼容性好。安装脚手架和创建项目的命令很简单npm install -g vue/cli npm create vitelatest mall-ui -- --template vue创建完项目后第一时间装依赖。商城后台必备的几样vue-router路由、pinia状态管理、axios请求、element-plus组件库。安装命令npm install vue-router4 pinia axios element-plus这里有个经验不要一次性npm install全部依赖而是每装一个就启动一次应用确认没有报错再继续。Vite 项目的报错信息大多数情况下很明确你一下子装十几个包出了问题反而不容易定位是哪个依赖和当前 Node 版本不兼容。开发阶段前端工程需要配置代理把 API 请求转发到后端网关避免开发时跨域。vite.config.js里可以这样写export default { server: { proxy: { /api: { target: http://localhost:8080, changeOrigin: true } } } }3.3 Vue Router 动态路由与权限菜单实现商城管理后台最核心的前端功能就是按照不同角色动态生成菜单和路由。普通做法是前端写死一套路由表用v-if藏起来但这种方式体验差且不安全。正确做法是登录后请求后端接口拿到当前用户的菜单权限数据然后通过 Vue Router 的addRoute方法动态添加路由。核心代码大致是这样的思路const router createRouter({ history: createWebHistory(), routes: baseRoutes }) export function initDynamicRoutes(menus) { menus.forEach(menu { router.addRoute({ path: menu.path, name: menu.name, component: () import(/views/${menu.component}.vue) }) }) }注意动态导入import()里面的路径不能是完全动态的字符串Vite 需要能静态分析出文件路径。比较稳妥的做法是维护一个组件映射表把后端返回的component字符串映射到实际组件对象。这个问题不解决你会在生产环境遇到“动态路由页面白屏”的经典 bug。3.4 把 Vue 打包进 Spring Boot 的两种姿势打包这件事网上问“vue打包放进springboot”的人特别多。我先说结论开发环境一定前后端分离但部署环境有两种方案。第一种是把 Vue 打包后的dist目录手动拷贝到 Spring Boot 的src/main/resources/static下然后启动后端浏览器直接访问后端的端口就能打开页面。第二种是更推荐的做法用 Nginx 部署前端静态资源用网关部署后端接口彻底分离。如果你选择第一种方案必须处理单页应用的路由刷新 404 问题。Vue Router 的history模式下访问/user/list刷新时后端路由表里没有这个路径会返回 404。解决办法是在后端写一个转发规则把所有非/api开头的路径都转发到index.html。这个配置我建议直接用 Nginx 做location / { try_files $uri $uri/ /index.html; }对于“手动拷贝 dist”这种笨办法还有一个进阶技巧用 Maven 的frontend-maven-plugin在构建后端前自动执行前端打包做到一条命令同时构建前后端。这样交付给别人的源码包对方只需要执行mvn clean package就得到了完整的可运行 jar体验非常顺滑。4. 监控、多环境与部署开源项目从能跑到管得好4.1 Spring Boot Admin 到底监控什么微服务拆分之后服务数量变多你不可能每个服务都打开日志看一遍。Spring Boot Admin 是解决这个问题的入门级方案它本身是一个 Spring Boot 服务负责展示各个服务实例的运行状态。每个业务服务只需要引入spring-boot-admin-starter-client并暴露了 Actuator 端点Admin 服务就能采集到健康状态、内存指标、线程信息等数据。实际操作中我强烈建议把 Actuator 的端点显式放开不要用默认配置。默认只开放health和info你需要的metrics、logfile、httptrace都是关闭的需要在配置文件里这样设置management: endpoints: web: exposure: include: *注意线上环境暴露所有端点有安全风险一定要配合 Spring Security 进行鉴权或者至少限制内网访问。Admin 的页面还能动态修改日志级别这一点在排查生产问题时非常实用不用重启服务就能临时把某个类的日志从 INFO 调到 DEBUG。4.2 配置中心与多环境管理微服务应用多了以后配置管理会变成一场灾难。每个服务的application.yml里都有一份数据库地址、Redis 地址改一个密码要登录十台服务器逐个改。用 Nacos 做配置中心之后配置统一管理并且支持动态刷新。这里有个版本差异要特别注意。Spring Cloud 2020.0 之前的项目会在bootstrap.yml里配置 Nacos 地址2020.0 之后的版本bootstrap.yml默认不加载了需要引入spring-cloud-starter-bootstrap依赖或者使用新的spring.config.import方式加载配置。很多人在升级版本后突然发现配置中心不生效十有八九是这个问题。多环境管理方面我习惯的做法是每个服务只保留一个主配置application.yml里面放非敏感配置然后通过 Nacos 的命名空间隔离 dev、test、prod 环境。数据库密码、对接第三方的密钥这些敏感信息放在 Nacos 配置里再配合 Nacos 的权限控制避免泄露。4.3 用 Docker Compose 一键搭建完整环境开源项目要让别人快速跑起来最友好的方式是提供docker-compose.yml。对于这种 Spring Boot Vue 的微服务项目整个依赖链至少包括 MySQL、Redis、Nacos再加上你自己的几个业务服务。写一个编排文件用户可以一条命令启动全部依赖。核心思路是这样services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root ports: - 3306:3306 redis: image: redis:7 ports: - 6379:6379 nacos: image: nacos/nacos-server:v2.2.0 environment: MODE: standalone ports: - 8848:8848 - 9848:9848 mall-gateway: build: ./mall-gateway ports: - 8080:8080这里有一个实际部署中很常见的坑容器内的服务名和端口不能随便写。比如mall-gateway容器里访问 Nacos地址应该写nacos:8848而不是localhost:8848因为容器隔离了网络。对第一次接触 Docker Compose 的人来说这个细节最容易卡住我建议在各个服务的配置脚本注释里把容器网络互相访问的规则写清楚能帮别人省很多事。5. 常见问题与避坑记录5.1 Spring Boot 版本与 Spring Cloud 版本的匹配表版本不匹配是微服务项目启动失败的头号原因。你发现项目启动时各种ClassNotFoundException、NoSuchMethodError第一反应应该是检查版本映射。我整理了一份常用对照Spring Boot 版本Spring Cloud 版本JDK 要求2.3.xHoxton.SR12JDK 82.6.x2021.0.xJDK 8/112.7.x2021.0.xJDK 8/113.0.x2022.0.xJDK 17我的建议是跟着你选定的开源项目本身的版本走不要自己乱升级。很多初学者觉得“版本越新越好”结果把 Spring Boot 从 2.6 升到 3.0发现原来的 Nacos、Sentinel 客户端都不兼容最后只能回滚。在实际生产环境里稳妥比新潮重要。5.2 跨域问题开发期和部署期分开解决只要前后端分离跨域就是避不开的话题。开发期最简单的方式是前端的 Vite 代理请求发到前端开发服务器由开发服务器转发到后端浏览器层面没有跨域所以不会报错。部署期如果前后端域名不同跨域问题的解决方式是在网关层统一加 CORS 配置而不是在每个业务服务里都加一遍。网关加 CORS 有一个细节值得注意GlobalCorsConfiguration需要配置 allowedOrigins但在生产环境我不建议用*因为*会让任何网站都能调用你的接口配合 Cookie 使用时会直接失效。要么用具体的前端域名要么配合网关鉴权一起做。这是很多人容易忽略的安全细节。5.3 Vue 打包后白屏、404、静态资源找不到这种情况我在很多开源项目的 issue 区见过无数次。常见原因有三类。第一类是路由模式配错了部署环境没做try_files的 fallback刷新子路由就 404解决办法就是前面说的 Nginx 配置。第二类是静态资源路径问题Vue 默认资源路径是/assets/xxx.js如果部署在子目录下就全白屏需要在vite.config.js里把base改成./或者对应子路径。第三类是接口请求地址问题前端打包后访问http://localhost:8080/api但后端接口不在同一个端口又没有配置代理请求全部失败表现为页面能打开但表格数据加载不出来。排查这类问题有个固定思路打开浏览器开发者工具看 Console 报错和 Network 请求。如果 JS/CSS 请求本身 404那就是路径问题如果请求发出去了但响应用 4xx/5xx那是接口问题如果你能定位到接口问题再去检查网关路由和后端日志一定会找到答案。5.4 后端启动报错“找不到 mapper”或“无法解析占位符”这种报错常见于你把公共模块里的通用代码引入业务模块之后。比如在mall-common里放了一个 MyBatis-Plus 的公共配置业务服务启动时它尝试扫描所有包但在其他模块里找不到对应 Mapper 接口就会报Invalid bound statement。解决办法是在每个业务服务启动类上显式指定扫描路径MapperScan(com.xxx.mall.user.mapper)不要图省事扫描整个项目的根包。同名问题还有配置文件的占位符解析失败。Value(${xxx.xxx})引用了不存在的配置项启动时直接报错。这时候你要检查这个服务是否引入了配置中心的配置文件以及 Nacos 里是否真的存在这个 key。如果项目从单体改造而来经常会出现一个服务还在引用已经迁移到别的服务的配置项。5.5 前端显示 PDF、播放 m3u8 这类特殊需求商城后台里经常会有商品详情页上传 PDF、视频预览这类需求。搜索词里出现“vue image能显示pdf吗”“vue播放m3u8免安装”说明很多人在这两个地方卡过。先说 PDF浏览器原生支持iframe或object标签预览 PDF但缺点是跨域限制严格而且不同浏览器 UI 不一致。想统一体验可以用pdfjs-dist这个库纯前端渲染 PDF稳定性很好代价是要写一点代码处理分页和缩放。再说 m3u8 视频流这种格式是 HLS 直播/点播的标准格式浏览器原生不支持播放它。常见电商场景里商户上传的短视频往往转成了 m3u8 格式前端要播放就需要引入hls.js。具体用法不复杂创建Hls实例后加载视频地址绑定到video元素就行而且它还支持 MSE 自动兼容效果比硬套一个播放器组件好。这类需求虽然偏四线但一旦碰上知道该用什么库能少走不少弯路。最后说点个人体会把 Spring Boot Vue 的微服务开源项目完整跑通只是第一步。我自己的体会是读这类项目源码时不要沉浸在一个个方法里而是先抓三样东西目录结构怎么分的、组件之间怎么互相调用的、网关层怎么把请求分发出去的。这三件事理清楚你对微服务的理解会比看十篇“高并发架构”文章都有用。多商户商城这类项目最值得学习的设计其实都在“看似多余”的地方。比如扣减库存为什么要走专门的库存服务支付回调为什么要做幂等处理这些细节才是项目经历两三年迭代后沉淀出来的东西。如果你准备拿这类开源项目作为二次开发的基础务必先确认你拉下来的是哪个版本分支不同的微服务版本技术栈差距可能非常大。最后再分享一个小技巧启动微服务项目时别一上来就跑全部服务。先启动 Nacos等注册中心就绪再启动网关最后启动业务服务并观察日志确认注册成功。按这个顺序跑你能在五分钟内排查掉 80% 的环境问题。
返回列表