
做受灾区救援物资管理最让人头疼的不是写代码而是怎么把“物资、需求、库存、派发”这四件事在多个部门之间对清楚。传统做法是搞一个单体系统所有功能塞在一个工程里交付快但等物资种类一多、受灾点一分散需求变更就开始互相打架今天加一个物资批次属性明天改一个审批流后天又说要接入地图大屏你会发现小小的单体应用越来越难改。这个项目我选择用SpringBoot Vue SpringCloud全家桶把它做成了微服务分布式架构就是为了让每个环节都能独立演进、独立部署、独立扩容。整套系统围绕“受灾区域、救援物资、出入库、派发审批”四个核心实体来组织前端使用Vue做单页应用后端拆成了多个微服务整体走的是前后端分离加微服务网关的现代架构路线。这篇文章我会从架构设计、核心功能落地、前后端联调、以及我踩过的各种坑几个角度完整拆一遍希望对正在做类似微服务项目、或者准备把单体系统重构为微服务的同学有实际帮助。1. 项目整体设计与架构拆解1.1 为什么一个救援物资管理系统要拆微服务很多人会问这种管理系统的数据量也不大日均可能就是几千条物资流水拆微服务是不是有点用力过猛我的看法是拆不拆服务不能只看数据量要看协作规模、职责边界和未来扩展方向。受灾区救援物资管理的业务链路非常长物资接收要管供应商和捐赠记录质检要管物资批次和有效期库存要管多仓库多维度的实时余量受灾点申请要管审批流派发要管运输单和签收最后还要汇总统计做大屏展示。如果放在一个单体系统里这些模块共享一个数据库、一套实体类、一个开发分支逻辑上确实能跑但每个模块的改动都会互相影响。比如你在优化库存扣减时不小心影响到了审批模块的查询性能或者你在申请模块加了一个字段导致派发单无法正常生成。这背后其实是“高内聚、低耦合”的工程原则在起作用。微服务把物资基础数据、库存、审批、派发、统计这些业务拆开之后各服务只需要通过接口对外暴露能力服务内部怎么做优化、怎么改表结构其他模块完全不感知。对于这种带有公益性质的项目后续还很可能对接外部捐赠平台、物流系统、大屏展示系统微服务架构天然留好了扩展位。1.2 服务模块划分与服务边界我在做服务拆分时没有按照三层架构那种“controller、service、mapper”纵向拆法而是按照业务能力域横向拆。拆完以后主要有这几个服务服务名称核心职责主要表与数据网关服务统一入口、路由转发、鉴权过滤无需独立业务表系统服务用户、角色、菜单、登录鉴权user、role、menu、user_role物资服务物资分类、物资档案、供应商、捐赠记录material_category、material_info、supplier、donation_record库存服务仓库、入库单、出库单、实时库存、库存流水warehouse、stock、stock_in、stock_out、stock_flow救援服务受灾点登记、物资需求申请、审批流程disaster_area、need_apply、need_apply_item、approve_record派发服务派发单生成、运输跟踪、签收确认dispatch_order、dispatch_item、dispatch_track统计服务多维度汇总物资、灾情、派发数据视情况可只做聚合查询这样拆完以后业务边界非常清楚物资服务不管库存库存服务不管申请申请服务不管派发。服务之间只通过OpenFeign接口调用不直接依赖对方的数据库表。这一点我觉得是整个项目能不能称得上“微服务”的关键如果你表面拆了服务底层还在共享库、互相join表那还是单体只是换了个部署方式。1.3 为什么选SpringCloud这套技术栈选SpringCloud核心原因是它在Java生态内和SpringBoot的契合度最高资料多、组件全、团队招人也容易。虽然现在也有一些轻量级方案比如Dubbo、gRPC但考虑到这个项目需要服务发现、负载均衡、配置中心、网关路由、限流熔断这些能力SpringCloud全家桶几乎是开箱即用。具体用到的组件和用途如下Nacos同时承担注册中心和配置中心的角色比Eureka加SpringCloud Config的组合少维护一个组件而且国内社区活跃遇到问题一搜就有答案。Spring Cloud Gateway统一流量入口做路由转发、Token校验、跨域处理比Zuul的性能好官方也一直推荐。OpenFeign服务间声明式HTTP调用配合Nacos做负载均衡写起来非常舒服。Sentinel限流与熔断降级在大量突发请求同时申请物资时非常有用防止下游服务被冲垮。Seata处理跨服务的分布式事务尤其是库存扣减、派发单生成这类强一致操作。Redis分布式锁、缓存热点数据、验证码存储。这套组合属于目前国内微服务项目里非常典型的选型既有SpringBoot的开发效率又有SpringCloud的治理能力业务、并发、运维三方面都照顾得到尤其适合做这类中大型管理系统。2. 后端核心功能与关键技术实现2.1 注册中心、网关与配置中心搭建要点这套微服务后端我建议从Nacos开始搭。先启动Nacos服务端然后在每个微服务模块里引入nacos-discovery和nacos-config两个依赖配置好服务名和注册地址即可。这里有个细节容易踩坑不同微服务的spring.application.name必须全局唯一Nacos就是靠这个字段区分服务实例的一旦重名服务会乱注册、调用直接搞错。网关模块是流量的总入口我保留了SpringCloud Gateway而没有把所有请求都直接打到各微服务。网关里做三件事第一路由转发把/api/auth/**转发到系统服务把/api/material/**转发到物资服务按路径前缀分流第二统一鉴权写一个全局过滤器解析JWT Token白名单接口直接放行非白名单接口校验失败就返回401第三跨域配置允许Vue前端所在的开发端口访问不然本地前后端联调会被CORS卡死。配置中心的优势在微服务环境里特别明显。比如库存服务的数据库密码要修改以前单体要改配置文件重新发布现在直接在Nacos配置中心改完配合RefreshScope注解服务运行期间就能热刷新。我习惯把每个服务分成bootstrap.yml和服务自己的application.ymlbootstrap里只放连接Nacos的必要信息剩余配置全部交给Nacos管理这样服务实例的配置就完全统一收口了。2.2 服务间通信与缓存设计服务间调用我用的OpenFeign声明一个接口加几个注解就能像调本地方法一样调远程服务。比如救援服务审核通过一个受灾点物资申请后需要通知库存服务锁定对应物资就通过库存服务暴露的一个Feign Client接口来实现。Feign接口的定义我强烈建议单独抽一个api模块或者公共模块来放不要在服务里散落定义否则服务调多了以后接口维护会变成灾难。我在设计时就是把Feign接口、DTO对象、通用返回体Result都放在一个叫common的模块里各个微服务统一依赖既避免了循环依赖也方便统一修改。缓存设计上物资分类、灾区列表这种读多写少的数据用Redis做缓存收益很高。我通常在Service层加一个简单的缓存逻辑先查Redis命中直接返回没命中就查数据库然后回写Redis并设置一个合理的过期时间。库存余量这种对实时性要求高的数据不建议轻易加缓存因为出入库操作非常频繁缓存一致性很难保证。如果确实要扛并发查询可以考虑用Redis的Hash结构存每个物资的实时余量在库存变更时同步更新读多写少的场景比查数据库高效得多。2.3 分布式事务处理最需要小心的部分跨服务调用最怕的就是“部分成功”A服务做完了B服务失败了数据就不一致了。在救援物资的场景里最常见的就是受灾点申请审批通过时要同时更新申请状态、锁定库存、生成待派发记录这一步跨了救援服务、库存服务、派发服务三个模块。一开始我使用的是本地消息表加定时任务补偿的方案后来发现实现成本并不低而且补偿逻辑写起来非常费神。后来改用Seata做全局事务通过GlobalTransactional注解来标示一个分布式事务的入口参与事务的每个服务用Transactional做本地事务Seata在底层通过分支事务注册和全局锁保证一致性。不过要提醒大家Seata对性能有一定损耗因为增加了额外的协调和锁开销所以在设计时要尽量控制分布式事务的边界。像物资申请审批这种核心链路可以用但像“记录一条操作日志”这种旁路操作就算失败也不影响主流程就没必要纳入全局事务。分清哪些需要强一致、哪些允许最终一致是微服务开发的基本功。2.4 分布式锁在库存扣减中的应用库存扣减这个场景如果并发一高很容易出现超卖的问题。比如某个仓库显示还有100顶帐篷两个受灾点同时申请90顶如果都先查询余量、再执行扣减不加锁就会直接扣成负数。单机时代用synchronized就能控制但在微服务架构下多个库存服务实例同时处理请求本地锁根本锁不住其他实例必须引入分布式锁。我用的方案是Redis分布式锁工具是Redisson。Redisson提供了一个封装好的RLock使用方式基本和ReentrantLock一致默认底层用Lua脚本保证了加锁和释放锁的原子性。核心代码如下Autowired private RedissonClient redissonClient; public boolean deductStock(Long materialId, Integer quantity) { String lockKey stock:lock: materialId; RLock lock redissonClient.getLock(lockKey); try { lock.lock(5, TimeUnit.SECONDS); Stock stock stockMapper.selectByMaterialId(materialId); if (stock.getAvailableQuantity() quantity) { return false; } stock.setAvailableQuantity(stock.getAvailableQuantity() - quantity); stockMapper.updateById(stock); return true; } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } } }这里有个关键点锁的粒度不要用整个方法锁而是锁到具体资源上。一个物资对应一个锁key不同物资的扣减互不干扰并发能力比全局锁高很多。锁的过期时间也要设置合理Redisson默认的看门狗机制会自动续期但如果业务执行时间确实可能超过预期还是建议显式设置一个合理的过期时间避免锁长期不释放拖垮性能。3. 前端Vue设计与核心模块实现3.1 前端工程初始化与目录规划前端我选择Vue 2.7配合Element UI来写不是不想上Vue 3和Element Plus而是这套微服务项目整体后端用的Java生态版本比较成熟前端如果采用Vue 3 Vite Pinia整套新生态很多人接手时反而会陌生。当然如果你是全新项目且团队没有历史包袱直接上Vue 3完全没问题组合式API配合TypeScript写起来体验更好。我推荐的Vue项目目录结构是这样的src ├── api // 接口请求封装按业务模块拆分文件 ├── assets // 静态资源 ├── components // 公共组件 ├── layouts // 布局组件侧边栏、顶栏、内容区 ├── router // 路由配置 ├── store // 全局状态管理 ├── utils // 工具函数请求封装、鉴权、格式化 └── views // 页面组件按菜单模块组织这个结构的好处是职责清楚api目录独立后前端和后端接口对接变得很方便新增一个功能只需要在api文件夹加一个模块文件再在views里加对应页面即可。请求封装是我特别在意的点axios实例统一设置了baseURL、超时时间和请求拦截器在请求拦截器里自动带着Token在响应拦截器里统一处理401跳转到登录页。3.2 路由、布局与权限控制路由设计上我先把静态路由和后端返回的路由分开。登录页、注册页、404页这类基础页面放在静态路由直接用物资管理、灾情管理、审批管理等业务页面则是用户登录后从后端动态获取菜单和路由权限再拼接生成。Vue Router的动态路由实现不难用户在登录接口拿到Token后再调用用户信息接口获取角色和菜单列表前端根据菜单数据通过addRoutes方法把业务路由动态注册进去。这样做有个好处不同角色登录后看到的侧边栏完全不一样管理员能看到全部菜单而普通的物资管理员只能看到物资和库存相关页面。页面权限只是第一步按钮级别的权限控制在这个系统里也有必要。我使用的是自定义指令v-permission在按钮上标记需要的权限码如果当前用户的权限码列表不包含该值就自动把按钮移除或禁用。比如普通管理员看不到“审批通过”按钮只有具备approve权限的角色才能看到这样前端就做了一层比较细的控制。当然真正的安全性还是靠后端接口鉴权前端控制主要是提升使用体验别把这两者搞反。3.3 物资管理页面的关键交互实现物资管理页面是整个系统里工作量最大的部分我拆成了物资档案维护、物资入库登记、物资出库登记、库存查询、库存流水五个子页面。物资档案维护用的是标准表格加弹窗表单的组合ElTable加ElDialog表单里根据物资类型动态展示不同表单项。这里有一个细节很值得注意捐赠物资和采购物资的必填项不同捐赠物资要记录捐赠方信息和证书编号采购物资要记录供应商和采购单价所以我在前端做了一个表单模型切换的逻辑避免一个表单里塞太多无关字段。入库登记页是库存数据的源头表单提交后要先调物资服务校验物资编码是否存在再调库存服务做入库操作。我在这里做了一个联动优化当输入物资编码后自动带出物资名称、规格、单位这些信息减少录入工作量。库存查询页面使用了分页表格加搜索条件条件包括仓库、物资分类、库存状态、关键字等后端同时做了多条件组合查询前端把搜索表单的字段直接拼到请求参数里即可。3.4 与后端联调跨域、网关和接口规范前后端联调阶段最常遇到的拦路虎就是跨域问题。我在SpringCloud Gateway里做了统一的跨域配置生产环境上线后跨域问题通常不在浏览器这一侧但本地开发时Vue默认跑在8080端口网关跑在8088端口如果不做处理浏览器会直接拦截接口请求。本地开发时我推荐利用Vue CLI的devServer.proxy配置做代理转发把/api前缀的请求全部转发到网关地址。这样浏览器看到的所有请求都来自同一源完全规避跨域也顺便处理了Cookie问题。后期如果换成nginx部署也是同样的思路把前端静态文件和后端网关入口放在同一个域名路径下。前后端接口规范上我约定统一请求响应结构Result包含code、message、data三个字段后端所有接口都按这个格式返回前端响应拦截器也按这个结构做统一处理联调效率明显高了很多。还有一个易踩的坑是时间格式。Java后端默认序列化的LocalDateTime可能是“2026-06-01T12:00:00”这种UTC格式前端直接展示非常难看。解决方法是后端在application.yml里配置全局Jackson时间格式统一给定yyyy-MM-dd HH:mm:ss格式前端在展示层再配合dayjs做二次处理双保险才能保证全系统时间显示统一。4. 核心业务场景实现从物资入库到灾区派发4.1 物资入库与库存模型设计物资入库是整个系统的数据入口做得不严谨后面所有环节都会出问题。入库流程我设计成两大块一是物资档案登记二是入库单创建。档案登记是基础数据的维护比如帐篷、棉被、饮用水、应急药品每种物资有独立的分类、规格、单位、有效期规则入库单则是一次实际的入库操作记录了本次入库数量、来源、经办人、存放仓库。库存模型我采用的是“一物一库一数量”的明细模式也就是每个仓库中每种物资对应一条库存记录包含总入库量、已出库量、当前余量、锁定数量。当前余量和锁定数量分开非常重要当一个受灾点申请通过后对应物资要先锁定而不直接扣减这样既保证了分配给该灾区的物资不被别人占用又能在后续派发确认时再做真正的扣减。如果审批通过了就扣减但派发单迟迟没有出库会形成账面上的“假缺货”。具体来说我在入库时要做三件事写入库单主记录、写入库明细、更新库存记录。这三步必须放在一个事物里而且要只更新明细数量并且提高库存版本从而确保并发情况下数据不会被覆盖。在实现上可以在单机事务中确保这三步成功而在微服务介入后如果用库存服务本地事务依然能保障库内一致性跨库一致性再由分布式事务处理。4.2 受灾点申请与多级审批受灾点登记后遇到物资紧缺就可以发起申请。我这里设计的申请单包含申请类型、紧急程度、申请物资明细列表、用途说明、附件照片。紧急程度我分了“一般、紧急、特急”三档审批策略里会根据紧急程度自动调整过期进一步办理。审批流是最能体现工作流思想的地方。我设计了“乡镇提交、区县审核、市级批准”的三级审批逻辑不同层级的审批人只能看到待自己审批的单据。审批过程中可以批准、驳回、退回补充材料每次操作都会记录审批日志方便回溯责任。这个模块在微服务架构中横跨了救援服务和派发服务一个关键实现细节是审批通过后需要同时做三件事——把申请单状态改成“已通过”调用库存服务锁定物资生成派发单草稿。这三件事不能拆开执行否则就是数据不一致因此我的做法是在审批通过的Service方法上加GlobalTransactional保证跨服务操作的一致性。4.3 派发单与物流跟踪审批通过后派发服务开始工作。派发单关联申请单、受灾点、物资明细、起始仓库和目的地同时生成一个唯一的派发单号规则设计成日期加流水号比如PD20260601001。派发单生成后物资管理员开始拣货出库。出库时系统会再次校验当前库存是否足够如果之前锁定的数量小于实际库存则不需要额外扣减如果因其他消耗导致库存不足系统会提示管理员修改出库数量。配送过程通过一个物流跟踪列表来记录运输状态出库、在途、到达、签收四个阶段。我在物流跟踪里做定时任务扫描在途超过一定时间的派发单通过WebSocket向前端推送预警消息提醒管理员及时联系物流确认情况。这个功能虽然实现不复杂但实际使用中评价很高因为这解决了物资到了哪里、有没有人跟踪这个老大难问题。4.4 灾情大屏与统计报表大屏展示往往是这类系统里最有视觉冲击力的部分但也是最容易被忽视技术难点的部分。灾情大屏我做了底层数据汇总服务和前端大屏页面两层。汇总服务定时从物资服务、库存服务、救援服务、派发服务拉取数据聚合后写入统计表或直接缓存到Redis前端大屏通过ECharts渲染各类图表。大屏的主要图表包括全国及区域受灾点分布地图、各灾区物资需求量与分配量柱状图、实时库存总量仪表盘、物资出库趋势折线图、待处理申请数排行表。地图使用ECharts的地图组件数据源是统计服务提供的JSON格式区域统计数据。这里有一个实践心得大屏数据不要每次实时查各服务的数据库。一方面是因为实时聚合对数据库压力较大容易把核心业务库拖垮另一方面大屏通常只要近一天或近一周的汇总数据。我采用定时聚合加Redis缓存的方式大屏接口直接读缓存刷新频率最低可到5秒一次既保证了用户体验又不影响核心业务性能。5. 常见问题与排查实录5.1 SpringBoot与SpringCloud版本匹配问题版本匹配是我做这个项目时浪费时间最多的问题之一。SpringCloud是一个总称它下面有很多组件版本号是伦敦地铁站名命名的对应关系很容易混乱。我自己遇到过最经典的一个坑项目用了SpringBoot 3.2.4但去网上复制了一段SpringCloud Alibaba 2021.0.4的依赖配置结果Nacos起不来、配置加载失败报错信息也是晦涩难懂。这是因为SpringBoot 3.X要求SpringCloud使用2022.0.X以上版本同时也要求SpringCloud Alibaba使用2022.0.0.0-RC2以上的对应版本老的版本底层依赖了对老Java版本的支持和新SpringBoot完全不兼容。我整理了一个简单的对应原则SpringBoot 2.7.X 搭配 SpringCloud 2021.0.X 和 SpringCloud Alibaba 2021.0.4.0JDK 8可跑。SpringBoot 3.2.X 搭配 SpringCloud 2023.0.X 和 SpringCloud Alibaba 2023.0.1.0JDK 17及以上。如果不是有特殊需求我建议稳妥优先直接选SpringBoot 2.7 JDK8兼容性最好遇到的奇葩问题最少。还有一点是依赖版本不要乱加。SpringCloud全家桶通常只要在父POM里用dependencyManagement统一声明版本子模块引入依赖时就不用写版本号这样能避免大量潜在的版本冲突。我见过太多人在这里偷懒结果引入两个版本的Jackson或Netty编译期没问题运行期各种NoSuchMethodError。5.2 分布式锁常见失效场景分布式锁我踩过的坑主要有三个都很典型。第一个坑是锁未设置过期时间导致死锁。如果业务代码在拿到锁之后抛了异常而finally块中的释放逻辑因为逻辑太早返回或者其他原因没有执行锁就会一直存在。后来我养成一个习惯所有锁必须加上过期时间参数同时把业务代码单独抽方法确保try-finally释放锁的逻辑始终被执行。第二个坑是锁的粒度太粗。最开始我把整个库存扣减方法锁住操作任何一个物资都会阻塞其他物资的扣减。听起来不严重但高峰期几十个申请同时进来响应时间急剧上升。后面改成针对物资ID加锁粒度细化成每个资源一把锁性能提升非常明显。第三个坑是锁过期业务还没有执行完。正常情况下Redisson的看门狗会持续续期但如果是网络抖动或者看门狗线程卡顿锁可能提前释放另一个线程就会进入临界区导致超卖。针对这个问题的解决思路有两个一是在数据库层面做一个乐观锁兜底更新库存时带上版本号条件版本不匹配就返回失败二是把锁的过期时间设为一个足够大的值同时依赖看门狗续期减少提前释放的概率。我最终的方案是两者结合数据库乐观锁兜底才是保命的底牌。5.3 服务间超时、重试与日志排障微服务链路一长问题排查的难度会直线上升。一个申请审批接口可能要调用两三个服务如果不做日志和链路追踪出了问题根本定位不到是哪一环挂掉。我的做法分三层。第一层是基础日志所有服务统一日志格式包含时间、服务名、接口路径、调用方、耗时、业务ID日志文件按天切割统一收集。第二层是链路ID网关在请求进入时生成一个traceId放到请求头的X-Trace-Id字段里传给下游的所有服务。每个服务的日志框架里都打印这个traceId排查时用同一个traceId去各服务日志里一搜整个链路就走完了。第三层是可观测性有条件的话引入SkyWalking直接在界面里查看调用链的耗时分布定位慢接口非常直观比人肉看日志效率高一个量级。另外Feign调用的超时配置也很容易踩坑。默认的Feign超时时间比较短遇到某个下游服务慢查询时就会报Read timed out。我一般会在配置里显式设置连接超时和读取超时ribbon: ReadTimeout: 8000 ConnectTimeout: 3000超时配置不能拍脑袋写要根据业务的实际耗时来。比如审批接口内部要处理分布式事务涉及三步跨服务调用那等待时间就至少得留5到8秒。如果配置太短业务明明在进行中客户端已经报错重试很容易造成重复提交的问题。针对这个问题我还做了一个防重的幂等处理在提交申请、审批通过这类关键接口上用请求的全局唯一ID加上Redis的SETNX做幂等校验前端提交时带上唯一流水号后端遇到重复流水号就直接返回上一次的处理结果。5.4 前端Vue的坑路由、打包与接口对接前端最容易遇到的问题首先是动态路由刷新后白屏。addRoutes方式有个先天不足刷新页面时Vue Router重新初始化而动态添加的路由还没有注册就会匹配不到路径直接进404。我的解决方案是在main.js里拦截路由跳转如果当前有Token但路由表为空就重新调用动态路由接口注入完毕后再执行原跳转。这个循环要控制在两到三次以内避免无限重定向。其次是打包部署问题。Vue打包后是把所有静态资源生成到dist目录放到nginx里要注意静态资源的路径配置。如果项目部署在域名根路径base路径设成/如果部署在子路径比如/admin那vue.config.js里的publicPath就得配置成相对路径或具体的子路径否则页面能打开但静态资源全部加载失败。另外这个项目里我在大屏页面用了SSE服务推送消息这个在Vue里使用EventSource实现。这里有一个常见的坑EventSource默认不带任何自定义Header如果你的网关鉴权依赖Token放在Header里那么SSE连接就会直接401。我的解决方案是在网关层对SSE的接口路径做白名单处理将登录态改为通过Cookie传给网关或者提供一个短期有效的专用SSE连接Token避免刷新连接时反复登录。6. 部署优化与项目经验6.1 用Docker Compose部署整套微服务实际部署环节, 手工一条条启动服务非常不靠谱。项目里服务数量多依赖的中间件也很多Nacos、Redis、MySQL一上环境部署就变得繁琐无比。我最终选择用Docker Compose统一编排整套环境。我的部署方案大概是这样的写一个docker-compose.yml里面定义MySQL、Redis、Nacos、Sentinel Dashboard这几个基础设施中间件以及各个微服务的容器。每个服务的镜像使用Dockerfile构建基础镜像是maven Eclipse Temurin JDK 17版本然后通过mvn package把jar包打进镜像。启动时通过environment变量或挂载配置把各服务的Nacos地址、数据库连接传给容器。version: 3.8 services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root123456 MYSQL_DATABASE: rescue_cloud ports: - 3306:3306 volumes: - ./mysql-data:/var/lib/mysql redis: image: redis:7-alpine ports: - 6379:6379 nacos: image: nacos/nacos-server:v2.2.3 environment: MODE: standalone ports: - 8848:8848 - 9848:9848 gateway: build: ./gateway ports: - 8088:8088 depends_on: - nacos部署顺序也值得一提先启动MySQL和Redis再启动NacosNacos注册成功后再启动各个业务服务。如果顺序反了服务启动时会因为拉取不到注册中心或者数据库连不上而反复重启。我一般会在docker-compose里加上depends_on同时在各微服务入口加一个启动等待逻辑数据库没就绪就不急着注册到Nacos。6.2 几个实测有效的性能优化方案这个项目做性能优化时我经历了从“能用”到“抗造”的过程中间有几个实操方案特别值得分享。第一个是数据库连接池。微服务拆分后每个服务都有自己的连接池配置如果配置得过小高并发场景下第一个瓶颈就是数据库连接不够。我统一把HikariCP的最大连接数调到20最小空闲连接数调到5没有再大主要是考虑到数据库总连接数有限各服务加起来不能超过数据库的max_connections上限。第二个是列表接口的分页与条件优化。物资流水、审批记录这种列表数据量大了以后全表count再join会非常慢。我对大数据量列表做了三个优化放弃多层join改为单表查询加内存组装对start_time、status等查询条件建立联合索引把冷数据按月归档到历史表只保留近三个月的活跃数据在热表。优化之后列表接口的响应时间从2秒降到了200毫秒左右。第三个是大屏聚合的定时任务设计。统计服务定时聚合用的是xxl-job在每天凌晨统计昨天的数据在每小时的半点统计最近24小时的数据同时支持手动触发重新统计。定时任务的执行时间尽量错开业务高峰不然会和核心业务抢数据库资源。6.3 做这类系统的一些心里话做这类受灾区救援物资管理系统触动我的地方恰恰是技术和业务结合的那部分系统好不好用真的关系到救灾时能不能及时把物资送到需要的人手里。技术上的微服务拆分、分布式事务、缓存、锁说白了都是为了这个终极目标服务。当你把一个看似平凡的管理系统做成微服务架构的时候你会发现设计的乐趣在于权衡拆得太细运维成本和接口开销上来了拆得太粗又会沦落到变了味的单体。我在这套系统里最满意的一个决策就是只拆了六个服务没有为了微服务而微服务每个服务都有独立的业务价值边界。对比我以前做的“十四个微服务”的项目那才是真的苦不堪言服务之间互相调用绕得跟蜘蛛网一样。落地过程中我也意识到微服务对团队和运维能力是有要求的。如果是一个三五人的团队没有专职运维那轻量级架构甚至单体加缓存可能反而更稳妥。但如果你就是想深入掌握SpringCloud这套分布式技术栈或者要做毕业设计、竞赛项目那这套结构是一个特别好的练习场它能强制你把分布式事务、分布式锁、服务治理、前后端分离这些硬核知识点全部过一遍。最后提一个小建议不要迷信微服务也不要害怕微服务。一切设计决策都要基于业务的真实痛点来做。如果你现在正好要做一个类似的项目一定要先把业务边界理清楚再动手写代码架构设计阶段多花一天后面编码和调试能省下一周。这套受灾区救援物资管理系统我从需求分析到部署上线大概用了六周时间架构设计和数据库建模占了将近一半时间但事实证明投入得非常值。