news 2026/10/5 10:57:45

微服务架构下超市库存管理系统开发复盘:SpringBoot+Vue+SpringCloud实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
微服务架构下超市库存管理系统开发复盘:SpringBoot+Vue+SpringCloud实战

微服务分布式SpringBoot+Vue+SpringCloud的超市购物采购库存管理系统——这是一类很典型的“听起来高大上、落地全是坑”的项目,我之所以想专门写一篇总结,是因为这套系统几乎把微服务开发里最常见的几个硬骨头都踩了一遍:服务怎么拆分、远程调用怎么管、库存扣减怎么保证不超卖、分布式事务到底用不用得上,以及前端怎么配合后端推着走。说实话,外面能找到的教程很多,但真正把“超市采购+库存管理”这个业务场景和微服务技术结合起来、而且能跑通的完整复盘并不多。

如果你是准备做毕业设计、或者想在公司内部搭建一套类似的中小型微服务管理系统,这篇内容会非常对口。我先放一个结论:这不是一个“必须微服务”的项目,但只要你决定用了微服务架构,核心难点就不在CRUD上,而在拆分边界、数据一致性和库存并发这三件事上。我会按自己实际开发这套系统的顺序来写,前半部分讲架构思路,中间部分讲核心业务实现,后半部分全是踩坑实录,尽量让你看完直接能上手复现。

1. 项目整体设计与思路拆解

1.1 先搞清楚:超市采购库存系统为什么要走微服务

很多人的第一个疑问是:一个超市购物、采购、库存管理系统,数据量也不至于到海量并发,为什么非要用SpringCloud做大堆微服务?我的看法是——不要为了微服务而微服务,但如果你想学微服务、或者公司技术栈已经有微服务规划,这个业务域是很好的练兵场。

拆开标题看,这个系统可以分成三个大业务域:购物(前台销售/购物车/订单处理)、采购(供应商/采购单/入库审核)、库存(库存台账/盘点/预警/出库扣减)。这三个域天然有数据交互,比如购物下单要扣库存,库存不足要触发采购建议,采购入库又要回写库存数量。这种业务结构在单体时代就是一个大数据库里的几个模块,在微服务时代却必须彻底分家,否则所谓微服务就是改了个名。

我当时给这套系统的定位是“按业务域拆服务,按流程拉协作”,拆出来的服务清单非常常规,但每一处拆分都有明确理由:

  • 用户/权限服务:负责登录、角色权限。这个是几乎所有管理系统的底座,独立出来能避免其他服务都来查用户表。
  • 商品服务:商品基本信息、分类、品牌、上下架、价格策略。商品数据是购物和库存的基础,所以它的数据要被多个服务读取。
  • 订单/购物车服务:前台购物车的增删改查、订单创建、订单状态流转。订单与用户、商品、库存都有耦合,是最容易出问题的服务。
  • 采购服务:供应商管理、采购计划的生成、采购单审核、采购入库单。采购服务和库存服务的交互非常频繁。
  • 库存服务:商品库存台账、出入库记录、库存锁定与扣减、库存预警、盘点单。这是整个系统的核心,也是并发压力最大的服务。
  • 网关服务:统一入口,负责路由转发、鉴权、限流。前端所有请求打到这里,再分发到各个业务服务。

这一拆就发现了两件很重要的事:

第一,服务拆分后的“分布式查询”才是日常开发的主体工作量。比如前端商品列表页,要显示商品名称、库存数量、当前售价,这在单体系统里一条SQL关联三张表就出来了,微服务里要分别调商品服务、库存服务,然后自己组装。你得学会接受这种“慢且繁琐”的组装过程,并在设计接口时提前考虑怎么减少跨服务调用的次数。

第二,拆分后数据一致性责任完全变了。原来的事务由数据库保证,现在每个服务有独立的库,单据流转、库存扣减这些跨库操作只能靠接口调用来协作,一旦某个环节失败,你需要自己回答“怎么回滚”或者“怎么补偿”。这是后面分布式事务那一节要解决的重点。

1.2 技术选型时的核心考量:版本适配比功能多更重要

技术栈看起来是不需要思考的:后端SpringBoot+SpringCloud,前端Vue,数据库MySQL,缓存Redis。但真正开始搭工程,我发现这套组合最容易出问题的不是业务代码,而是版本兼容性。

我建议的直接参考搭配:

  • 后端基础框架:SpringBoot 2.7.x + SpringCloud Hoxton.SR12(或与SpringBoot版本严格配套的Cloud版本)
  • 注册中心与配置中心:Nacos 2.x(用作服务发现+配置管理,可以少部署一个服务)
  • 服务调用:OpenFeign + Ribbon负载均衡(SpringCloud 2020以后Ribbon被Spring Cloud LoadBalancer替代,但用旧版本组合更稳)
  • 网关:SpringCloud Gateway(不要用Zuul,维护性和性能都差不少)
  • 缓存与分布式锁:Redis 6.x + Redisson客户端
  • 数据库:每个微服务独立库,MySQL 8.x
  • 消息中间件:ActiveMQ(如果暂时不想引入Kafka/RabbitMQ,ActiveMQ就足够处理采购入库、库存变更通知这类削峰需求)
  • 前端:Vue 2.x + Vue Router + Vuex + ElementUI,请求封装用Axios

这么一套配置下来,可以保证在Win/Mac本机开发时不会再出现“SpringCloud组件启动秒挂”这种常见问题。因为SpringCloud和SpringBoot的版本绑定关系非常强,不同大版本的API差别很大,网上很多教程用的是旧版的依赖,照抄到高版本项目里就会报各种各样的兼容错误。版本适配最稳的办法就是直接用Spring官方提供的Spring Initializr生成基础工程,再对照SpringCloud官方版本说明选择对应的Cloud版本号。

版本的坑我后面专门用一节来说,这里先给个提醒:不要看到新版本就升级,SpringCloud这个生态要的是整套兼容,不是单个组件最新。

2. 微服务架构下的核心实现:从拆分到部署

2.1 服务清单与数据库边界划分

标准微服务的第一原则就是数据独立,所以每个服务必须独占自己的数据库或至少独占表。开发中最常见的设计错误就是把所有库表都放在同一个MySQL实例下,然后号称“分了微服务”,这种做法会让服务间随时可以跨库join,等于没有拆分。

我当时设计的库表边界如下:

服务名称独立数据库核心表
用户权限服务db_usersys_user、sys_role、sys_permission、sys_user_role
商品服务db_productproduct_info、product_category、product_brand、product_sku
订单服务db_ordercart_info、order_info、order_item、order_status_log
采购服务db_purchasesupplier_info、purchase_order、purchase_order_item、purchase_instock
库存服务db_stockstock_info、stock_record、stock_lock、stock_warning、stock_check

这里有个非常细节但很关键的问题:商品服务有product_info表,库存服务有stock_info表,两边都有“商品ID”,但商品名称、价格等属性只在商品库里。库存服务接收采购入库单时,只知道要管哪个商品ID的库存,并不需要商品名称,所以设计接口时库存服务的入参只需传商品ID、数量、批次等库存域数据,避免跨服务拿商品详情。但前端展示库存列表的时候,又需要商品名称,怎么办?我的方案是:页面的库存列表接口由网关聚合或前端并行请求,库存服务返回“商品ID+库存量”,前端查完再批量调商品服务获取名称和规格信息。这种“数据冗余不冗余、接口怎么聚合”的设计,比代码本身更考验架构功底。

2.2 网关路由与统一鉴权:怎么把服务串起来

SpringCloud Gateway是整个系统对外的唯一入口,所有请求统一先到服务网关,再路由到对应的微服务。虽然服务间调用用OpenFeign是直接走服务名的,但对外暴露的接口必须通过网关转发。好处是你可以把鉴权、日志、限流都集中在网关做,而不用每个服务重复实现。

网关这块我的配置核心思路有两点:

第一,路由规则要按服务拆分,不要按页面拆。比如所有 /api/user/** 转发到用户服务,/api/product/** 转发到商品服务,/api/stock/** 转发到库存服务。前端封装axios时,统一baseURL指向网管地址,目录结构按模块分,方便维护。

第二,Token校验别放在网关内部直接查数据库,而是用Redis。用户登录成功后,用户服务生成JWT并把用户信息写入Redis,网关从请求头取出Token,先去Redis校验登录态,再通过Token里的userId、role信息做路径级权限校验。这样做的好处是:网关不依赖用户服务的接口,避免了高频鉴权请求全部打到用户服务导致的服务过载。

路径权限这块我建议用如下规则:接口URL中带上角色前缀或逻辑前缀,比如/api/admin/**和/api/stock/**分开,网关里配置一个简单的路径-角色映射表。大型项目可以用Shiro或SpringSecurity做成细粒度权限,但超市管理系统的角色数量有限,Admin、采购员、仓库管理员、收银员,用网关层做粗粒度校验、服务内部做细粒度数据隔离就够用了。

2.3 前端Vue如何配合微服务的多模块布局

前端Vue和微服务后端配合时,最大的变化就是页面视角从“单系统”变成了“多模块协作”。前端不再是直接请求业务服务,而是统一请求网关。Vue侧应该先把接口封装层做清晰。

我在前端做的目录结构参考:

src/ api/ // 统一接口封装 user.js product.js order.js purchase.js stock.js router/ index.js // 路由配置,含动态路由逻辑 permission.js // 路由守卫 store/ modules/ user.js // 用户状态 app.js // 应用状态 views/ login/ dashboard/ product/ purchase/ stock/ order/ utils/ request.js // axios封装,带token、错误拦截

Vue的路由配置有一个非常值得注意的点:动态路由。传统后端管理系统的路由表是写死在Vue代码里的,但微服务多模块下用户权限不同,你希望登录后只显示他有权限看的菜单和页面,所以要用路由守卫动态挂载。我的做法是:

  • 基础路由只放登录页和404页面,其余页面全部在登录成功后,根据后端返回的菜单权限数组动态生成路由,用router.addRoutes()挂载。
  • 菜单权限数组由用户权限服务下发给前端,比如仓库管理员返回的菜单只有“库存管理”、“采购入库”、“库存预警”,采购员返回“采购管理”、“供应商管理”。

这样前端菜单不再是一刀切,配合网关的接口权限校验,实现了“前端隐藏菜单+后端拒绝请求”的双重保障。很多初学者只做前端路由隐藏,后端接口默认全开放,结果接口被人直接构造请求就能访问,这是很严重的安全漏洞。

2.4 服务间调用的坑:OpenFeign超时与熔断

服务间同步调用用的是OpenFeign,但用上之后你会发现它没你想的那么省心。最大的坑是默认的超时时间非常短,而跨服务调用经常因为网络抖动、数据库慢查询等原因耗时超过默认超时时间,结果订单服务调库存服务超时,整个下单流程直接失败。

我的Feign核心配置经验如下:

ribbon: ReadTimeout: 5000 ConnectTimeout: 3000

这组配置的意思是连接超时3秒,读取超时5秒。但更要紧的是给Feign加熔断降级。库存服务如果宕机了,订单服务不能一直干等着报错,而应该快速失败并给用户提示“库存服务暂不可用”。实现方式是FeignClient的fallback类里返回一个兜底结果,比如扣减库存失败时,订单直接标记为待重试状态。

我建议你在设计接口时,把Feign调用模块单独抽出来做一个包,把fallback集中管理,避免精力分散导致某个服务漏掉降级策略。

3. 核心业务实现:采购、购物、库存的完整闭环

3.1 采购流程设计与数据流

超市采购比较好理解:库存低于预警值就需要发起采购申请,采购员联系供应商下单,供应商送货后仓库管理员做入库操作,最后库存台账更新。这个流程的难点不在单张表上的CRUD,而在跨服务的数据流转。

我设计的采购流程大概是:采购服务根据库存预警消息生成采购计划 -> 采购员提交采购单 -> 采购单通过审批后,采购服务生成“待入库”状态的采购入库单 -> 仓库管理员在采购服务里确认入库,采购服务立刻调库存服务的“入库”接口,库存服务更新库存台账并记录流水。

这里面最需要注意的一点是不要用订单服务和采购服务直接操作库存表,所有库存变动必须走库存服务提供的接口。原本可能觉得“反正都是自己项目的代码,直接改库存表多简单”,但这样会把库存的核心逻辑散落到多个服务中,后续排查和涨库时会非常痛苦。

3.2 购物下单与库存扣减:为什么必须用分布式锁

购物下单是库存系统的高频场景。用户在收银台下单时,系统需要先锁定库存,再创建订单,最后扣减库存。如果在高并发下两个用户同时购买了同一个商品,就可能导致超卖。传统单体系统用数据库行锁或悲观锁能解决,但微服务环境下,库存扣减发生在库存服务里,订单创建发生在订单服务里,任何一方操作数据库都只能锁住自己服务的那张表。

这里就是分布式锁登场的地方。我用了Redisson来实现,因为Redisson对Redis分布式锁的封装已经很完善,天然支持看门狗机制自动续期,不需要自己再写复杂的续期逻辑。扣减库存的核心代码片段大概长这样:

RLock lock = redissonClient.getLock("stock:lock:" + skuId); try { // 尝试获取锁,最多等3秒,锁过期自动释放时间设30秒 if (lock.tryLock(3, 30, TimeUnit.SECONDS)) { // 先查库存是否充足 int stock = stockMapper.selectStockBySkuId(skuId); if (stock < deductNum) { throw new BusinessException("库存不足"); } // 扣减库存并更新库存台账 stockMapper.deductStock(skuId, deductNum); stockRecordMapper.insert(new StockRecord(skuId, deductNum, "SALE")); } else { throw new BusinessException("系统繁忙"); } } finally { lock.unlock(); }

我特别想强调几个不能忽略的地方:

  • 锁的粒度一定要细到SKU级别,而不是整个库存服务的锁。如果锁的是全局库存,那这个超市的收银系统性能会差到离谱,“A商品扣库存”会把“B商品扣库存”也阻塞住。
  • 事务必须包含锁内操作。一开始我只把“查询库存”放进了锁里,扣减操作在事务里提交,结果发现线程A查询库存10件、线程B也查询库存10件,然后A扣完B再扣,照样超卖。所以查询和更新必须在同一把锁内完成。
  • 如果不用Redisson,手动写Redis的SETNX要注意设置过期时间,否则锁释放不了就死锁了。很多老项目踩过这个坑,Java新手尤其容易漏掉。

3.3 库存预警与采购建议:业务闭环的触发点

这套系统里我个人最喜欢的一个功能是“库存预警自动触发采购建议”,因为它在微服务架构下把多个服务串了起来,充分体现了分布式系统的协作能力。实现思路是这样的:

  • 库存表里维护每个SKU的库存上限和下限。每天定时任务扫描库存台账,凡是库存量低于下限的SKU,生成预警记录。
  • 库存预警记录发到消息中间件(ActiveMQ),采购服务监听这个队列,自动生成采购计划草稿,里面带上商品ID、建议补货数量、当前库存量、建议供应商。
  • 采购员在采购模块看到自动生成的采购计划草稿,修改确认后提交审批。

这个流程的亮点在于库存服务和采购服务之间完全解耦了。库存服务不用关心采购服务怎么处理预警,采购服务也不用关心库存服务是怎么判断预警的。以后如果要把ActiveMQ换成Kafka或者RabbitMQ,只需要改消费者端,上游逻辑一点不用动。

使用ActiveMQ时,我建议开启手动ACK机制。默认自动ACK下,消费者拿到消息但还没处理完程序就崩了,消息已经被确认丢失,预警就永远没了。手动ACK下,处理完业务逻辑再确认消息,遇到异常可以不确认或重新入队,保证消息不丢。

3.4 分布式事务:超市系统真的需要Seata吗

分布式事务这块,我先说结论:这套系统,我用的是“可靠消息+最终一致性”的方案,没有引入Seata,因为业务场景根本不需要强一致性。很多人一看到跨库操作就条件反射地说“上Seata”,但Seata的AT模式会带来明显的性能损耗和复杂度上升,如果业务场景能容忍最终一致,就不该用它。

每一次购物下单的流程都涉及订单库和库存库,如果订单创建了但库存扣减失败,怎么办?我的方案是:

  • 订单服务先创建订单,状态设为“待扣库存”。
  • 异步调用库存服务扣库存,扣减成功再更新订单状态为“已付款/待发货”。
  • 如果扣减失败,订单留在“待扣库存”状态,通过定时任务扫描并重试扣减。
  • 扣减成功的标志是库存记录里生成流水号,订单表里也记录这个流水号,实现幂等。

这就是典型的本地消息表+定时重试的最终一致性方案。虽然订单在极端情况下会有短暂的状态不一致(可能用户看到“处理中”),但在这个系统里完全可以接受,因为超市的下单流程本来就不是毫秒级强一致的场景。

如果非要用Seata,我建议只在采购入库这种强一致的场景下使用,比如“更新采购入库单状态+增加库存”必须同时成功,因为一旦不一致,账实就会对不上。但即便用,也建议把Seata的全局事务范围控制到最小,避免把好几个服务拉进一个大事务里,导致锁范围和事务时间都失控。

4. 分布式系统下常见问题与排查实录

4.1 库存扣减与订单创建的“超级大坑”:还不是分布式锁那么简单

很多人觉得库存扣减只要加了分布式锁就万事大吉,但实际还遇到过一个非常隐蔽的问题:下单接口是“先查库存再锁库存再扣库存”,但在事务提交之前锁就已经释放会怎样?虽然锁是加在了方法上,但如果你用的是@Transactional放在外层、锁代码放在内层,事务还没有提交锁就释放了,另一个线程进来读到的是旧库存数据,一样会超卖。

解决的办法是把事务和锁放到同一个层次里,或者用编程式事务手动控制提交点。我的经验是:对于库存扣减这种小事务,干脆不要依赖注解事务,直接用编程式事务监控提交状态,确保“锁内查询-扣减-事务提交”一气呵成。

还有一个细节,如果扣减库存和生成库存流水记录在同一个事务里,那你必须在锁里把两个写操作都做完。不要为了缩短锁持有时间,只把扣库存放锁里,流水记录放到锁外面,因为一旦流水插入失败,库存扣减已经提交,数据就不完整了。

4.2 服务调用超时与Feign降级导致的“幽灵订单”

我在联调阶段遇到过最诡异的问题:用户在前台提交了订单,但订单服务显示“扣库存失败,订单已取消”,可库存服务那边却显示扣了库存。查到最后发现,库存服务其实成功扣了库存,但响应返回到订单服务时因为网络延迟超过了Feign的读取超时时间,订单服务瓜分降级逻辑把订单取消了,但此时库存已经扣了。

这个问题特别典型,直接暴露了同步调用在分布式环境下的不确定性。后来我做了两处处理:

  • 库存服务的扣减接口改为先更新库存状态,扣减成功后直接返回明确结果,保持响应速度在可控范围。
  • 订单服务收到降级结果后,不直接取消订单,而是把订单置为“待扣库存待确认”状态,通过补偿任务去查库存服务确认实际扣减结果,然后根据真实结果流转订单状态。

这种“先标记、后确认”的思想,比“一刀切失败回滚”要可靠得多,因为分布式环境下的失败并不代表真的失败,可能只是响应丢失,得通过查询和幂等重试来消除不确定性。

4.3 并发扣减时数据库行锁与Redis锁叠加,性能反而下降

我在压测阶段发现,库存服务的QPS上不去,用参数分析后发现每个请求都要先抢Redis锁,抢到锁后进入数据库事务,MySQL又会对库存行加行锁。也就是说,Redis分布式锁和数据库行锁是双重锁,请求量一大,Redis锁排队的线程也排队,数据库行锁也排队,整个扣减接口的吞吐量被拉低了很多。

后来我对库存扣减做了优化:去掉数据库行锁的依赖,把“乐观锁”用起来。也就是在库存表的更新语句里直接加上库存数量条件:

UPDATE stock_info SET stock = stock - #{num} WHERE sku_id = #{skuId} AND stock >= #{num}

如果影响行数为0,说明库存不足或版本冲突,再走分布式锁重试一次。这样的好处是,绝大多数请求可以在不排队的情况下直接更新成功,只有极少数并发冲突才会用到分布式锁做重试。Redis分布式锁变成了兜底方案,而不是每笔扣减都要走这个重量级路径。

这个优化让扣减接口的TPS提升了将近一倍,顺便也减少了数据库行锁持有时间。想把系统跑顺,并发控制一定要分清层级,不要把每个请求都往重型锁里塞。

4.4 网关内网端口暴露问题:一次偶然的“越权访问”事故

曾经有一次,团队里有人为了图方便,把商品服务的端口直接暴露在防火墙规则里,然后前端联调时直接访问商品服务IP端口,绕过了网关。这么做最直接的影响是网关的鉴权和限流完全失效,任何知道这个IP端口的人都能直接访问商品服务内部接口,如果商品服务里有一两个“管理端接口”没做好权限校验,就很容易出现越权风险。

微服务架构下,业务服务端口必须只在内网可见,对外只能暴露Gateway端口。我在部署时用防火墙或安全组规则限制了各微服务端口的访问来源,只允许Servlet容器所在网段互相访问,而公网只放行网关端口。这个经验看似基础,但在实际开发中很容易被合作同事打破。

4.5 常见问题排查速查表

这一章节做个小结,把我在开发中遇到的最值得记录的固定问题整理成一个速查表:

现象可能原因排查命令或手段解决方案
服务启动后注册不到NacosNacos版本与SpringCloud版本不匹配,或配置文件namespace不对查看Nacos控制台服务列表;检查bootstrap.yml统一版本配套;显式指定namespace和group
Feign调用报Connection refused服务未启动,或服务名大小写不敏感,或负载均衡未生效curl http://服务名/actuator/health;查Feign的日志确认服务健康检查通过;配置正确服务名
下单总是显示“库存不足”缓存中的库存数据和数据库不一致对比Redis缓存和MySQL库存;看是否有订单未回滚缓存删除或主动刷新;核对扣减接口的幂等性
分布式锁明明加了还是有超卖锁粒度不是SKU级;锁未覆盖事务提交抓取日志看锁key;分析AOP执行顺序锁key加SKU;锁内手动控制事务提交
采购预警重复生成消费者收到消息后ACK失败导致消息重复消费看消息日志中是否有重复记录;检查消费端幂等消费端加消息唯一键去重;采用数据库唯一索引
前端菜单不显示权限接口返回的菜单code与前端路由name不一致打印权限接口返回值;检查路由表name统一前后端菜单编码规范
库存服务一个SQL慢导致整套订单接口慢库存表数据量增大且没有索引,同时锁等待严重使用慢SQL日志,查看执行计划加索引;拆大事务;读写分离

5. 实操经验:从开发到部署的完整过程复盘

5.1 本地开发环境搭建的细节

开发微服务项目,第一步就是搞定本机运行环境。有些细节不处理清楚,后面会反复在启动上报错。

  • Nacos必须配持久化,不然重启掉配置。我当时因为嫌麻烦,直接用了Nacos默认的内嵌数据库,结果改了几次配置后服务重启,配置文件丢了一部分,排查了半天。建议直接把Nacos切换到MySQL持久化。
  • 统一用Maven多模块工程。一个父pom管理所有微服务模块,每个服务是一个子module,公共的entity、dto、utils抽成一个common模块。JAVA开发中Maven的必要性不用多说,父工程一定要锁定SpringCloud和SpringBoot版本,子模块不要再写死版本,不然就等着解决依赖冲突吧。
  • 本地联调时Feign调用报DNS错误。 可能是Nacos服务列表里注册的是容器IP或虚拟机IP,配置注册IP时要指定为本机局域网IP。这个在application.yml里加一行配置就可以解决。

5.2 数据库初始化与初始化数据管理的技巧

微服务拆分后数据库初始化变成了一件麻烦事。单体系统一个init.sql搞定全部表,微服务就得每个服务建自己的库。

我建议使用Flyway来管理每个微服务的数据库脚本,每个服务模块的resources/db/migration目录放各自的SQL脚本。这样在新环境部署时,直接启动服务就能自动执行建表脚本,不用再手工去跑SQL。迁移脚本命名要有纪律性,比如V1.0__init_schema.sql、V1.1__add_stock_record_index.sql,否则团队协作时会产生大量冲突。

5.3 一次全链路联调的大教训:服务调用链的日志追踪

微服务下查问题非常依赖日志和链路追踪。我第一次联调时,因为每个服务打印日志格式不一样,用户反馈“提交订单报错”后,查了整整半天才定位到是库存服务里一个空指针。后来才意识到,担心是不该省的钱:必须引入TraceId。

做法很简单,网关在入口生成一个UUID作为traceId,放到请求头里,各个服务在拦截器里取出这个traceId,然后写入日志的MDC上下文。这样查日志时,用同一个traceId一搜就能把整个调用链看全。如果不想引入SkyWalking这类重链路系统,至少要把日志格式里的traceId统一做好。

后来我发现,这种方式对于排查超时、降级、幂等重复这类问题几乎是“救命级”的,强烈建议所有人做微服务前就先考虑好。

5.4 部署方式选择:要Docker吗

如果只是本地演示或者毕业设计,不部署Docker也可以,用java -jar逐个启动也能跑。但如果你想体验真正的分布式部署,我建议用Docker Compose把Nacos、Redis、MySQL、ActiveMQ以及各个微服务编排起来,在一台机器上模拟多节点。

一个需要注意的点是,SpringBoot服务镜像的构建要特别注意时区和JVM参数。国内环境下不加时区配置,日志时间会差8小时。我在Dockerfile里固定加上:

ENV TZ=Asia/Shanghai

以及JVM启动参数里设置堆大小和GC参数,避免容器默认配置导致OOM。真正的生产部署还会涉及环境变量管理、CI/CD流程,但如果你只是做到Docker化这一步,已经比90%的课程设计有说服力了。

6. 关于这个项目的一些个人体会

这个系统开发下来,我最大的体会是:做微服务项目,真正的技术难点不是那些看上去很炫的组件,而是“边界”和“状态”这两个极其朴素的词。边界指的是模块怎么分、库表怎么分、哪些数据归哪个服务管;状态指的是订单、库存、采购单这些核心数据的流转,要设计好在任何步骤失败时都能处理。

如果你正在准备做类似项目,我建议你先把核心业务(用户、商品、订单、库存、采购)的建模和状态图画清楚,再开始写代码。架构图也好、SpringCloud组件也罢,都是你理解了业务之后才能用好的工具,而不是反过来让工具拖着业务走。

另外记住一点:这套系统不一定需要微服务,但既然选了微服务,就必须把服务间的数据一致性、分布式锁、超时与降级这些真实的工程问题处理好。处理好了,它就是一份叫得响的完整项目;处理不好,它就是一个披着微服务外衣的CRUD系统,面试时一问就会露馅。希望这篇博文能帮你少走一些我已经走过的弯路。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/5 10:57:45

学术引用量排序利器:Publish or Perish从入门到精通

先说句实在话。搞学术的人&#xff0c;不管你是刚进门的研究生还是已经带团队的老师&#xff0c;只要碰过文献综述、期刊投稿或者学者评价&#xff0c;就迟早会撞上一个问题&#xff1a;一篇论文、一个学者、一本期刊到底有多大影响力&#xff1f;光靠谷歌学术网页版一个个点进…

作者头像 李华
网站建设 2026/10/5 10:57:43

Paraview Python Calculator Filter:从入门到绘制时变曲线

做CFD或者有限元后处理的人&#xff0c;多半都跟Paraview打过交道。这个开源神器能处理的数据量级和可视化效果&#xff0c;放在商业软件里也是第一梯队。但真要说日常使用频率最高的功能&#xff0c;除了切割面、做流线&#xff0c;剩下的就是各种过滤器了。Paraview里过滤器一…

作者头像 李华
网站建设 2026/10/5 10:56:43

柔性作业车间调度遗传算法:初始化与编码策略详解

柔性作业车间调度&#xff0c;遗传算法的第一个坑&#xff1a;初始化和编码做车间调度优化的朋友&#xff0c;十有八九都会撞上柔性作业车间调度问题&#xff08;FJSP&#xff09;。这东西比经典作业车间调度&#xff08;JSP&#xff09;难在哪&#xff1f;JSP里每道工序的加工…

作者头像 李华
网站建设 2026/10/5 10:56:17

Java毕设全攻略:Spring Boot+MySQL打造智慧种植管理系统

每年到了毕业季&#xff0c;"Java毕设"这四个字就成了搜索框里的常客。尤其是像"基于Spring Boot的智慧农作物果园农产品蔬菜种植管理系统"这种标题&#xff0c;你随便一搜就能看到一大堆带源码、带MySQL脚本、带论文、甚至承诺"全bao"的链接。很…

作者头像 李华
网站建设 2026/10/5 10:56:16

Cursor接入前端开发实战:从安装配置到AI结对编程全指南

做了快十年的前端开发&#xff0c;从jQuery时代一路走到Vue、React、TypeScript全栈&#xff0c;说实话我对“AI写代码”这件事一开始是持怀疑态度的。直到我把Cursor完整接入日常工作流&#xff0c;才真正意识到&#xff1a;这不是一个自动补全工具&#xff0c;而是一场前端开…

作者头像 李华
网站建设 2026/10/5 10:55:34

SpringBoot+Vue供应商管理系统开发与毕业论文实战指南

如果你正在为“基于SpringBootVue的供应商管理系统”这个毕业论文题目发愁&#xff0c;这篇文章应该能帮上忙。我这两年完整带过几个同类型课题&#xff0c;也从零跟过供应商管理项目的企业落地&#xff0c;从选题拆解、数据库设计、前后端联调&#xff0c;到论文排版、查重降重…

作者头像 李华