Spring Cloud Alibaba电商微服务实战:Nacos+Gateway+多数据源
2026/9/13 11:14:35 网站建设 项目流程

简介:这是一套面向计算机专业本科生的高分毕业设计级分布式网上商城系统,适用于课程设计、期末大作业及毕设参考,帮助学习者系统掌握Spring Cloud微服务架构与Vue前后端分离开发实践。资源包共792个文件,涵盖115个Java后端核心代码、45个Vue组件页面、164个JS逻辑脚本、53个CSS样式文件及1个完整SQL数据库脚本,辅以bat部署脚本、yml配置文件和论文文档,整体压缩包仅26.25MB,轻量易部署。已有108人下载学习,说明其调试充分、开箱即用——项目已通过导师验收,含可直接运行的源码、MySQL 8.0建库脚本、Navicat可视化支持及配套毕业论文,详细阐述了分布式服务拆分逻辑、JWT鉴权实现、购物车与订单状态机设计等关键模块,结构清晰、注释规范,便于理解微服务通信机制与前端路由管理。

1. 这不是又一个“商城Demo”,而是一套可跑通、可调试、可延展的分布式电商骨架

你下载的这个.zip文件,表面看是“高分毕业设计”,实际承载的是 Java 工程师从单体走向分布式的第一块真实跳板。它不依赖 Docker Compose 一键启动的黑盒封装,也不用 Spring Boot Admin 做花哨监控——而是用最朴素的spring-cloud-starter-alibaba-nacos-discovery+spring-cloud-starter-gateway+spring-cloud-starter-openfeign组合,在本地 Windows 或 Linux 环境下,手动拉起注册中心、网关、用户服务、商品服务、订单服务、支付服务共 6 个独立进程,并让 Vue 前端通过 Nginx 反向代理访问后端 API。MySQL 不是只建三张表,而是按业务域拆分user_dbproduct_dborder_db三个物理库,每库含sharding_key字段支撑未来分库分表。论文里写的“熔断降级策略”不是空话,对应到代码就是@SentinelResource(fallback = "fallbackMethod")的真实调用链路。适合刚学完 Spring Cloud 五大组件(Eureka/Nacos、Ribbon/LoadBalancer、Feign、Hystrix/Sentinel、Zuul/Gateway)但还没在真实项目里串起来的同学;也适合需要快速验证分布式事务、服务间鉴权、前端路由守卫与后端 JWT 拦截联动机制的中级开发者。


2. 搭建 Nacos 注册中心与配置中心:Spring Cloud Alibaba 的落地基座

2.1 为什么选 Nacos 而非 Eureka?关键在配置热更新与命名空间隔离

Eureka 已进入维护模式,Nacos 不仅提供服务注册发现,更原生支持配置管理(Config),且能通过namespace实现开发/测试/生产环境配置物理隔离。毕业设计中常被忽略的点是:服务注册名必须小写且无下划线(如user-service),否则 Feign Client 会因 Spring Cloud 默认的ServiceInstanceListSupplier解析失败而报No instances available for user-service。Nacos 控制台默认账号密码为nacos/nacos,首次登录后务必修改——这是线上部署前的安全必选项。

2.2 下载与启动 Nacos Server(以 2.2.3 版本为例)

# Linux/macOS 下执行(Windows 请用 cmd 替代) wget https://github.com/alibaba/nacos/releases/download/2.2.3/nacos-server-2.2.3.tar.gz tar -zxvf nacos-server-2.2.3.tar.gz cd nacos/bin # 单机模式启动(开发用),-p 指定端口避免冲突 ./startup.sh -m standalone -p 8848

提示:若提示JAVA_HOME not set,需先配置 JDK 17(Spring Cloud 2022.x 要求 JDK 17+)。Windows 用户请确保JAVA_HOME指向 JDK 根目录,且Path中包含%JAVA_HOME%\bin

启动成功后访问http://localhost:8848/nacos,登录后创建两个命名空间:

  • dev(ID 自动生成,如e9a5b3c1-xxxx-xxxx-xxxx-xxxxxxxxxxxx
  • test(同上)

再在dev命名空间下新建配置:Data ID = user-service-dev.yamlGroup = DEFAULT_GROUP,内容如下:

spring: datasource: url: jdbc:mysql://localhost:3306/user_db?useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true username: root password: 123456 driver-class-name: com.mysql.cj.jdbc.Driver

此配置将被user-service启动时自动加载,无需硬编码数据库连接信息。

2.3 在 user-service 中集成 Nacos Discovery 与 Config

pom.xml必加依赖(注意版本对齐):

<dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId> <version>2022.0.1.0</version> <!-- 对应 Spring Cloud 2022.0.x --> </dependency> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId> <version>2022.0.1.0</version> </dependency>

bootstrap.yml(注意是 bootstrap,不是 application):

spring: application: name: user-service cloud: nacos: discovery: server-addr: localhost:8848 namespace: e9a5b3c1-xxxx-xxxx-xxxx-xxxxxxxxxxxx # dev 命名空间 ID config: server-addr: localhost:8848 namespace: e9a5b3c1-xxxx-xxxx-xxxx-xxxxxxxxxxxx file-extension: yaml group: DEFAULT_GROUP

启动UserApplication后,刷新 Nacos 控制台「服务列表」,应看到user-service实例状态为UP。此时服务已注册成功,且配置已生效。


3. 构建 Spring Cloud Gateway 网关层:统一入口、路由转发与 JWT 鉴权

3.1 网关为何不能只配路由?必须处理跨域与 Token 透传

Vue 前端运行在http://localhost:8080,后端服务注册在 Nacos,网关作为唯一对外入口,需解决三个核心问题:

  • CORS 跨域:浏览器同源策略拦截,不能只靠@CrossOrigin注解(该注解作用于 Controller 层,网关层无效)
  • JWT Token 透传:前端登录后存Authorization: Bearer xxx,网关需将其原样转发给下游服务,而非自行解析后丢弃
  • 路由路径重写:Vue Router 使用 history 模式,请求/api/user/info应转发至user-service/user/info,需去除/api前缀

3.2 gateway-service 的核心路由配置(application.yml)

spring: cloud: gateway: routes: - id: user-service uri: lb://user-service predicates: - Path=/api/user/** filters: - StripPrefix=1 # 去掉 /api - DedupeResponseHeader=Access-Control-Allow-Origin Access-Control-Allow-Credentials # 解决跨域响应头重复 - id: product-service uri: lb://product-service predicates: - Path=/api/product/** filters: - StripPrefix=1 globalcors: add-to-simple-cors-configuration: true cors-configurations: '[/**]': allowed-origins: "http://localhost:8080" allowed-methods: GET,POST,PUT,DELETE,OPTIONS allowed-headers: "*" allow-credentials: true max-age: 3600

注意:lb://表示 LoadBalance,即从 Nacos 获取user-service实例列表并负载均衡;StripPrefix=1是关键,否则user-service收到的请求路径仍是/api/user/info,而其 Controller 映射的是/user/info,导致 404。

3.3 实现 JWT Token 全链路透传(非鉴权,仅透传)

网关不校验 Token(鉴权由各微服务自己完成),但需确保Authorization头不被过滤。Spring Cloud Gateway 默认会删除部分敏感 Header,需显式放行:

@Configuration public class GatewayConfig { @Bean public GlobalFilter customGlobalFilter() { return (exchange, chain) -> { ServerHttpRequest request = exchange.getRequest(); // 强制保留 Authorization 头 ServerHttpRequest newRequest = request.mutate() .header("Authorization", request.getHeaders().getFirst("Authorization")) .build(); return chain.filter(exchange.mutate().request(newRequest).build()); }; } }

此 Filter 确保下游user-service能通过request.getHeader("Authorization")获取原始 Token,用于后续JwtUtil.parseToken()解析。


4. Vue 前端与后端服务的联调要点:Axios 拦截器、路由守卫与打包路径

4.1 Vue 项目中 Axios 的请求 baseURL 与代理配置冲突真相

很多同学在vue.config.js中配置了devServer.proxy,却在生产环境打包后发现 API 请求 404——根本原因是:proxy 仅在npm run serve开发模式下生效,打包后(npm run build)生成的静态文件由 Nginx 托管,proxy 不起作用。正确做法是:

  • 开发时:vue.config.js中配置 proxy,避免 CORS
  • 生产时:Nginx 配置反向代理,将/api路径转发至http://localhost:8081(Gateway 端口)

vue.config.js示例:

module.exports = { devServer: { proxy: { '/api': { target: 'http://localhost:8081', // Gateway 地址 changeOrigin: true, pathRewrite: { '^/api': '' // 开发时自动去掉 /api 前缀 } } } } }

4.2 登录后 Token 存储与请求拦截器实现

src/utils/request.js

import axios from 'axios' const service = axios.create({ baseURL: process.env.NODE_ENV === 'production' ? '/api' : '', // 生产环境走 Nginx,baseURL 为空;开发环境由 proxy 处理 timeout: 10000 }) // 请求拦截器:携带 token service.interceptors.request.use( config => { const token = localStorage.getItem('token') if (token) { config.headers.Authorization = `Bearer ${token}` } return config }, error => Promise.reject(error) ) // 响应拦截器:401 跳转登录页 service.interceptors.response.use( response => response, error => { if (error.response?.status === 401) { localStorage.removeItem('token') router.push('/login') } return Promise.reject(error) } ) export default service

注意:baseURL在生产环境设为空字符串,是因为 Nginx 已将/api路径代理到网关,前端请求/api/user/login实际发往http://your-domain.com/api/user/login,Nginx 再将其转发至http://localhost:8081/user/login。若此处误设为http://localhost:8081,则生产环境会跨域。

4.3 Vue Router 路由守卫与权限控制雏形

src/router/index.js中添加全局前置守卫:

router.beforeEach((to, from, next) => { const token = localStorage.getItem('token') const whiteList = ['/login', '/register'] // 白名单页面 if (whiteList.includes(to.path)) { next() } else if (!token) { next('/login') } else { // 检查 token 是否过期(简单版:解析 payload 中的 exp 字段) try { const payload = JSON.parse(atob(token.split('.')[1])) if (Date.now() >= payload.exp * 1000) { localStorage.removeItem('token') next('/login') } else { next() } } catch (e) { localStorage.removeItem('token') next('/login') } } })

此守卫实现了基础的登录态校验,比单纯检查token是否存在更可靠,避免过期 Token 导致后续接口 401。


5. MySQL 多库拆分与 MyBatis Plus 多数据源配置:为分布式事务打基础

5.1 为什么毕业设计要拆user_dbproduct_dborder_db?不只是为了“高大上”

单库单表在并发量 > 1000 QPS 时必然成为瓶颈。拆库的核心目的有二:

  • 物理隔离故障域order_db出现慢 SQL,不会拖垮user_db的登录接口
  • 为 Seata 分布式事务铺路:Seata 的 AT 模式要求每个业务库都建undo_log表,若所有表挤在一个库,undo_log无法按业务归属划分,事务回滚将混乱

三库结构示意:

库名主要表说明
user_dbsys_user,sys_role,sys_user_role用户认证与权限
product_dbpms_product,pms_category,pms_sku商品中心
order_dboms_order,oms_order_item,oms_payment订单与支付

5.2 MyBatis Plus 多数据源配置(以 user-service 为例)

user-service只需访问user_db,但order-service需同时访问order_db(主)和user_db(查用户信息)、product_db(查商品信息)。因此order-service需配置多数据源。

pom.xml加入:

<dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-extension</artifactId> <version>3.5.3.1</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.18</version> </dependency>

application-dev.yaml中定义数据源:

spring: datasource: dynamic: primary: order-ds # 默认数据源 strict: false datasource: order-ds: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/order_db?... username: root password: 123456 user-ds: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/user_db?... username: root password: 123456 product-ds: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/product_db?... username: root password: 123456

Java 代码中使用@DS("user-ds")切换数据源:

@Service public class OrderServiceImpl implements OrderService { @Autowired private OrderMapper orderMapper; @Autowired private UserMapper userMapper; // 此 Mapper 对应 user-ds @Override @DS("order-ds") public void createOrder(Order order) { orderMapper.insert(order); // 查询用户信息(跨库) User user = userMapper.selectById(order.getUserId()); // 自动走 user-ds // ... } }

提示:@DS注解需配合DynamicDataSourceRegister自动注册数据源 Bean,否则会报Could not determine embedded database driver class。完整配置见DynamicDataSourceConfig.java,核心是DataSourceRouter实现determineCurrentLookupKey()方法。

5.3 数据库初始化脚本执行顺序与依赖关系

解压包中的sql/目录下,必须按以下顺序执行:

  1. nacos-mysql.sql→ 初始化 Nacos 配置中心元数据表
  2. user_db.sql→ 创建user_db及表结构
  3. product_db.sql→ 创建product_db及表结构
  4. order_db.sql→ 创建order_db及表结构

特别注意:order_db.sql中的oms_order表含外键user_id,但未实际关联user_db.sys_user.id(跨库外键不生效),因此user_id字段仅作逻辑引用,应用层需通过 Feign 调用user-service接口获取用户详情。这是分布式架构的典型取舍:放弃强一致性外键,换取系统解耦与水平扩展能力。


6. 验证分布式链路是否真正打通:从浏览器 F12 到 Nacos 实例列表的全路径追踪

6.1 用 Chrome DevTools Network 面板确认网关路由生效

启动全部服务后,打开http://localhost:8080(Vue),点击「登录」按钮。在 Chrome 的 Network 面板中筛选XHR,找到POST /api/user/login请求:

  • Headers → General:查看Request URL是否为http://localhost:8080/api/user/login(前端发出)
  • Headers → Response Headers:确认access-control-allow-origin: http://localhost:8080存在,且vary: Origin字段出现(证明网关 CORS 配置生效)
  • Preview:返回 JSON 中含token字段,且Authorization头已出现在 Request Headers 中

此时,请求流为:
Vue (8080)Nginx (80)Gateway (8081)Nacos 查找 user-service 实例user-service (8082)user_db

6.2 在 Nacos 控制台验证服务健康状态与实例数

登录http://localhost:8848/nacos,进入「服务列表」:

  • user-service:显示 1 个实例,IP 为127.0.0.1,端口8082,状态UP
  • product-service:同上,端口8083
  • order-service:同上,端口8084
  • gateway-service:同上,端口8081

点击任一服务的「详情」,查看「健康实例数」是否为 1,「不健康实例数」为 0。若显示0,常见原因有:

  • user-serviceapplication.ymlspring.cloud.nacos.discovery.server-addr地址错误(如写成http://localhost:8848,应为localhost:8848
  • 防火墙阻止了8082端口(Windows 用户需在「高级安全 Windows 防火墙」中放行该端口)

6.3 日志串联:用 traceId 验证全链路唯一标识

user-serviceUserController.login()方法开头加入:

@GetMapping("/info") public Result<User> getUserInfo() { String traceId = MDC.get("X-B3-TraceId"); // Spring Cloud Sleuth 默认注入 log.info("getUserInfo traceId: {}", traceId); // ... }

启动时添加 JVM 参数启用 Sleuth(user-serviceVM options):

-Dspring.sleuth.enabled=true -Dspring.sleuth.web.skipPattern=(^/actuator.*|.*\\.png|.*\\.css|.*\\.js|.*\\.html|/favicon.ico|/webjars/.*)

然后在 Gateway 和 user-service 的日志中搜索traceId,应能在两处日志中找到完全相同的 ID,证明链路已贯通。这是排查分布式环境下性能瓶颈或异常的黄金线索。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询