☰
易物小店微服务实战:SpringBoot+Vue+SpringCloud架构设计
2026/10/2 3:25:23 网站建设 项目流程

前段时间帮朋友做一个易物小店的物品交换系统,简单说就是大家把闲置物品放上来,看对眼了直接申请交换,而不是买卖。这类系统最容易被低估,表面看是“发布物品 + 聊天工具”,但真要把并发、状态一致性、图片视频存储都理顺,踩的坑一点不比电商少。我最终选择了 SpringBoot + Vue + SpringCloud 的微服务方案,把系统拆成网关、用户、物品、交换、消息、评价六个服务,前端用 Vue 3 + Vite,中间件用 Nacos、Redis、RabbitMQ、MinIO。这篇文章把我从架构选型、核心实现到分布式锁/事务、前端联调、上线排障的过程整理出来,代码不会贴全,但关键设计思路和可复制的步骤都会展开,适合正在用 SpringCloud 做微服务项目、或者想自己搭一套交换类后端的朋友参考。

1. 先想明白:易物小店为什么需要微服务

1.1 从业务场景反推架构需求

很多人一听到“微服务”就先堆服务,结果服务拆了一堆,业务却跑不通。我做这个项目时是先画业务流程图,再倒推架构。易物小店和电商最大的区别是没有支付环节,但多了一个“双向履约”的动作:用户A发布一个闲置的蓝牙音箱,用户B看到了很喜欢,于是发起交换请求,A可以同意也可以拒绝;同意之后,两个人需要线下或通过平台确认完成。这个过程中没有钱款流转,但有物品状态变化、交换单状态变化、积分冻结、消息通知。任何一个环节出现并发冲突,都会出现两个人同时换到同一个物品的情况。

这种需求如果用传统单体,初期确实能跑,但等到要支持多人同时申请热门物品、要独立扩展消息推送服务、要让不同团队分别迭代物品和交易模块时,耦合会越来越严重。所以我把系统按业务域切成了六个服务:用户、物品、交换、消息、评价,外加一个网关。每个服务有自己的数据库表,服务之间通过 OpenFeign 或者异步事件通信。这样做的好处是,物品服务的发布逻辑不会因为交换状态变复杂而被拖垮,交换服务出了问题也不会把用户登录拖挂。

需要坦白说,如果你只是做一个 demo,单体完全够用。微服务的成本在于部署、监控、分布式事务、版本兼容这些额外负担。但如果目标是一个可以长期迭代、多人协作的中型平台,微服务的边界划分越早做越好。易物小店这种业务,“物品”和“交换”是核心域,“消息”和“评价”是支撑域,按这个维度拆,边界比较干净,不会出现服务之间循环调用。

1.2 服务拆分到底怎么切

拆分服务不是“多就是好”,而是要看数据边界和变更频率。我最后确定的服务列表如下:

服务名核心职责关键表端口
gateway-service路由、鉴权、限流无8888
user-service用户注册登录、积分账户、个人资料user, user_account9001
item-service物品发布、物品列表、物品状态、图片附件item, item_image9002
exchange-service交换单创建、状态流转、交换履约exchange_apply, exchange_confirm9003
message-service站内信、通知、简单聊天message, notification9004
review-service双方互评、信用分review9005

每个服务独立连自己的数据库,我实际用的都是 MySQL,但物理库分开。物品服务只关心物品本身,不需要知道交换单长什么样;交换服务通过 Feign 调用物品服务获取物品快照、调用用户服务校验账号状态。服务之间用 DTO 传递数据,禁止直接传实体对象,否则一旦表结构变化,所有下游都得跟着改。

拆分时最容易犯的错是把“用户上传的头像图片”和“物品图片”混在一个附件服务里。虽然表面上都是文件,但前者属于用户域,后者属于物品域。我一开始统一放 MinIO 里,路径上分目录,看起来没差,但到后面权限控制时才发现,物品图片要按物品状态做私有访问,用户头像可以公开读,混在一起权限规则很难写。后来干脆在 file 处理上只做一层存储抽象,业务服务各自生成预签名 URL,比较干净。

1.3 技术栈为什么选中这套

技术选型很大程度上决定你后面要踩多少坑。我用的是 SpringBoot 2.7 + SpringCloud 2021.0.x + SpringCloud Alibaba 2021.0.5.0。注册中心和配置中心用 Nacos,网关用 Spring Cloud Gateway,服务间调用用 OpenFeign,分布式锁用 Redisson,分布式事务用 Seata,缓存用 Redis,消息用 RabbitMQ,对象存储用 MinIO。前端是 Vue 3 + Vite + Pinia + Element Plus。

这套组合最大的好处是有大量现成的微服务脚手架可以参考,尤其是若依微服务 plus 这类开源项目,已经把用户、菜单、权限、定时任务都做好了,拿来做底座能省很多事。SpringBoot 提供业务开发效率,SpringCloud 负责分布式能力。没有选 Dubbo 是因为 Dubbo 更偏 RPC 通信,服务治理和网关配套还得另搭;SpringCloud Alibaba 对中文社区更友好,Nacos 控制台清晰,Seata 做分布式事务也很成熟。

前端选 Vue 而不是 React,一方面团队熟悉,另一方面 Vue 3 的组合式 API 写中后台非常顺手。易物小店的管理员后台、C 端 H5、卖家端小程序,这三类界面用 Vue 生态可以复用大量逻辑。Vite 开发热更新比 Webpack 快很多,配合 Axios 封装和动态路由,前后端联调效率很高。

2. 后端核心模块与关键实现

2.1 用户服务与统一认证登录

用户服务是基础服务,所有接口都要知道当前操作人是谁。我采用 JWT + 网关统一鉴权:用户登录成功后,user-service 签发 token,前端把 token 存到本地,请求时放在 Authorization 头里。网关解析 token,把 userId 放到请求头转发给下游服务。这样业务服务不需要重复解析 JWT,逻辑只收敛在网关层。

密码存储必须用 BCrypt,不要用 MD5 或者 SHA 明文盐。即便数据库泄露,BCrypt 的慢哈希也能拖延破解时间。注册时还要做邮箱或手机号唯一校验,否则一个用户开多个小号去“刷交换”。积分账户单独建表,用户注册时初始化一个可用积分账户。交换物品不一定是等值交换,双方可以约定用积分补差,所以冻结、扣减、退还积分的操作要有流水记录,方便对账。

JWT 密钥要放到 Nacos 配置中心,并支持动态刷新。如果你把密钥写死在每个服务里,轮换密钥时要改一堆服务然后重新发布。我最初就是写死的,后来为了安全要换密钥,结果还漏了一个服务,导致一段时间客户端拿到新 token 但网关不认,排查了半天才定位到。这个坑强烈建议避开。

2.2 物品服务:发布、图片与视频存储

物品表设计上要预留扩展字段,但不要过度。核心字段包括:id、user_id、title、description、category、condition_level、expected_exchange、status、created_at、updated_at。物品状态我用 AVAILABLE、HOLD、EXCHANGED、OFF。AVAILABLE 表示可被交换,HOLD 表示交换单创建后暂扣,EXCHANGED 表示已完成,OFF 表示用户主动下架。只有 AVAILABLE 状态能被发起交换,这个校验在接口层和数据库状态流转中都要做。

图片和视频存储用 MinIO,而不是直接丢到服务器磁盘。把 MinIO 整合进 SpringBoot 很简单,先配置连接信息:

minio: endpoint: http://localhost:9000 access-key: minioadmin secret-key: minioadmin bucket: swap-item-images

上传时生成对象名,我的策略是/物品ID/UUID.扩展名,避免中文名和重复名。这里要注意:不要把整个文件读进内存再传给 MinIO,要走流式上传,否则大文件会频繁 OOM。图片上传后顺手用 Thumbnailator 压缩一下,生成一个 webp 或缩略图,列表页用缩略图,详情页用原图。

聊到视频,易物系统初期不建议直接支持视频上传,成本很高。如果真有 m3u8 需求,做法是先转码成 HLS 切片,再把 .m3u8 和 .ts 文件传到 MinIO,前端用 hls.js 播放。MinIO 本身不关心文件格式,只关心对象存储和访问权限。Bucket 权限要设成私有,后端生成预签名 URL 给前端访问,这样能避免硬编码 URL 导致的东西被盗链。

2.3 交换服务:状态机是核心

交换服务是整个系统的核心,最容易乱的地方就是状态机。我把交换单状态定义为:

状态含义可流转到
INITIATED申请方已发起,等待物品方处理ACCEPTED, REJECTED, CANCELLED
ACCEPTED物品方已同意,等待双方确认CONFIRMED, CANCELLED
CONFIRMED双方确认交换COMPLETED, REPORTED
COMPLETED交换完成终态
REJECTED物品方拒绝终态
CANCELLED超时自动取消或手动取消终态

发起交换的接口是POST /exchange/apply,入参只需要 itemId 和备注。服务内部要做几件事:检查物品状态为 AVAILABLE,检查申请人不是物品发布人,检查用户账号状态,然后创建 exchange_apply 记录,再通过 Feign 调用物品服务把物品状态改成 HOLD。这里涉及两个服务的数据变更,后面会专门说分布式事务。

交换确认流程不能用前端按钮状态控制,后端每次状态流转都必须校验当前状态是否允许迁移。比如一个交换单已经被 REJECTED,攻击者直接调 confirm 接口想把状态改成 COMPLETED,接口必须拒绝。我加了一张状态流转历史表,记录每次变更的操作人和时间,一方面方便审计,另一方面定位线上 bug 时有日志可查。

2.4 消息服务与站内信解耦

交换流程中会产生很多通知:物品被申请时通知发布人,交换成功时通知双方,超时取消时通知申请人。如果这些通知都放在交换服务里同步发送,一旦消息服务卡顿或 RabbitMQ 阻塞,交换接口就会变慢。所以我用异步事件解耦:交换服务只负责发送 MQ 消息,message-service 消费后写库并推送 WebSocket。

事件消息体不要太大,只需要包含事件类型、相关业务 ID、接收人列表。比如“交换申请创建”事件,消息体就是{type: EXCHANGE_APPLY, exchangeId: 123, ownerId: 9, applicantId: 10}。message-service 拿到后组装站内信文案,保存到 notification 表,再通过 WebSocket 推送给在线用户。用户不在线也没关系,下次登录拉取未读列表即可。

如果后续要做实时聊天,最简单的方案是 WebSocket + Redis Pub/Sub。单机可以直接用一个 WebSocket 服务,集群时要通过 Redis 广播消息,让不同节点的连接都能收到。这个项目前期我直接用轮询接口每 10 秒拉一次未读数量,上线后觉得体验一般,才换成 WebSocket。所以这个模块可以先做轮询,保留升级接口。

3. 分布式场景下的三个坑:锁、事务、缓存

3.1 用分布式锁解决“重复发起交换”

易物系统虽然没有秒杀,但热门物品被多人同时申请时,并发压力不比秒杀小。前端虽然会在点击“立即交换”后把按钮置灰,但用户刷新页面、多端操作、脚本刷接口,都可能让后端收到多次请求。单体项目里可以直接用synchronized锁,但微服务多实例下,每个实例的锁是独立的,连点两个请求打到不同实例,照样会通过校验。

我在申请接口上用了 Redisson 分布式锁。锁的 key 分两级考虑:一个物品同时只能被一个交换单申请,所以先用物品维度锁,防止两个不同用户同时申请同一件物品;再用用户+物品维度锁,防止同一个用户重复申请。物品维度锁代码大致这样:

RLock lock = redissonClient.getLock("exchange:item:" + itemId); try { boolean tryLock = lock.tryLock(3, 30, TimeUnit.SECONDS); if (!tryLock) { throw new BusinessException("当前申请人数过多,请稍后重试"); } // 重新查询物品状态,若已是 HOLD 则拒绝 // 创建交换单,调用物品服务改为 HOLD } finally { if (lock.isHeldByCurrentThread()) { lock.unlock(); } }

这里有两个容易踩的坑。第一个是释放锁时一定要判断当前线程是否持有锁,否则可能把别人的锁给释放了。第二个是锁过期时间不能太短。我一开始把 leaseTime 设成 5 秒,结果业务里为了查库存、发消息、调 Feign,偶尔超过 5 秒,锁自动释放后另一个线程进来了,导致重复申请。现在要么把 leaseTime 设得足够长并加监控,要么用 Redisson 看门狗自动续期。如果业务逻辑简单,推荐 tryLock 按场景设置 10~30 秒,宁可等待期短一点,也不要锁内事务过长。

3.2 分布式事务:把交换流程变成最终一致

创建交换单涉及用户服务、物品服务、交换服务三处数据:用户服务要冻结积分,物品服务要把物品改成 HOLD,交换服务要插入 exchange_apply。如果其中一个成功、一个失败,数据就乱了。比如物品变成 HOLD,但交换单没建出来,那物品就永远没法再被交换;或者交换单建了,但用户积分没冻结,后面确认时积分又扣不动。

我用了 Seata AT 模式。在创建交换单的入口方法上加@GlobalTransactional,后续的本地事务和 Feign 调用都会自动纳入全局事务。Seata AT 模式对业务代码侵入很小,核心依赖是数据库的 undo_log 表,Seata TC 会记录事务执行前镜像,回滚时自动恢复数据。

使用 Seata 有两个注意点。一是一整个全局事务里不要做远程调用以外的长时间操作,比如发短信、计算图片压缩,这些会拉长全局事务,降低并发。二是 MQ 发送不能直接放到事务方法体内,因为事务还没提交,消费者可能提前感知到数据。更稳妥的做法是事务提交后发送事件,或者用本地消息表。我实际把消息发送挪到了交换单创建成功后的监听器里,用@TransactionalEventListener(phase = AFTER_COMMIT)触发,保证“先提交业务数据,再发通知”。

如果不想引入 Seata,也可以靠状态机和定时任务做最终一致。比如先把物品状态更新为“预占”,再创建交换单,如果创建成功就改成 HOLD,如果失败就通过定时任务把预占状态回滚。这个方案更轻,但状态分支比较多,对开发人员要求高。项目时间紧、追求稳定的话,用 Seata 是更省事的选择。

3.3 缓存:热点物品与搜索性能

物品列表和详情是读多写少的典型场景。第一次查询数据库,之后把数据放到 Redis,能明显降低数据库压力。我用的 Spring Cache + Redis 实现,物品详情接口加@Cacheable,更新物品后加@CacheEvict。

这里要处理三个经典问题。缓存穿透:有恶意用户或并发场景,请求一个不存在的 itemId,每次都穿透到数据库。解决方式是对空结果也缓存 5 分钟。缓存击穿:某个热点物品的缓存过期瞬间,大量请求同时打数据库。解决方式是用 Redis 互斥锁重建缓存,或者对热点 key 不设置过期时间,后台主动更新。缓存雪崩:大量物品同时过期,导致 DB 压力飙升。解决方式是在缓存过期时间上加随机值,比如 10 分钟到 15 分钟之间取随机数,避免同时过期。

易物系统最怕的不是缓存击穿,而是数据一致性问题。用户发起交换后,物品状态从 AVAILABLE 变成 HOLD,如果缓存没及时更新,其他用户仍然看到“可交换”,点击后会失败。所以我在状态流转的关键接口上必须主动删除对应物品缓存,不能只依赖@CacheEvict的方法。比如 exchange-service 通过 Feign 调用 item-service 更新状态时,返回最新状态,同时 item-service 内部要把缓存清掉。上线后我加了一个简单的核对任务:每 5 分钟检查 Redis 里状态为 AVAILABLE 但数据库中状态为 HOLD 的物品,自动刷新缓存,避免人为遗漏。

3.4 接口幂等与重复提交防护

分布式锁能挡住并发,但防不住极端情况下的重复请求,更可靠的是数据库唯一索引。我给 exchange_apply 表加了request_no唯一键。前端在发起交换时生成一个全局唯一的请求号,后端插入时如果重复,数据库会抛 DuplicateKeyException,捕获后返回“请勿重复提交”。

ALTER TABLE exchange_apply ADD UNIQUE KEY uk_request_no (request_no);

这个方案的好处是,即使分布式锁因为 Redisson 版本问题失效、或者网关重试导致同一请求发两次,数据库层面也会拦截。分布式锁是并发控制,数据库唯一索引是最后一道保障。两者配合,才能把重复交换单的概率降到最低。

还要注意接口层面的“重复”不一定是同一个请求号。用户连点两次,如果前端没有生成新 request_no,第二次会直接被唯一索引拦掉。如果用户刷新页面后再点一次,前端重新生成 request_no,那后端会认为是两个不同请求。这时靠锁和业务校验处理:同一用户对同一物品发起第二次交换,锁内会发现已经存在交换单,直接拒绝。所以前端在创建交换单前,也要查询“当前用户是否已对该物品发起过交换”,体验上能提前提示。

4. 前端 Vue 实现与联调细节

4.1 Vue 项目搭建与动态路由

前端我用了 Vue 3 + Vite。创建项目很简单:

npm create vite@latest swap-front -- --template vue cd swap-front npm install npm run dev

再安装 Vue Router、Pinia、Axios、Element Plus。页面结构分为几种角色:普通用户看到首页、物品详情、发布物品、我的交换、消息列表;管理员看到后台管理菜单:用户管理、物品审核、交换记录、评价管理。前端不能直接写死路由菜单,否则改权限要重新发版。我从后端登录接口拿用户角色和菜单列表,然后通过router.addRoute动态注册。

动态路由有个经典坑:刷新页面时 Pinia 状态被清空,动态路由也没了,用户一刷新就白屏。解决办法是在全局前置守卫里判断路由是否已加载,如果没有,先重新请求用户信息和菜单,再调用next({ ...to, replace: true })重新进入目标路由。代码逻辑大概这样:

router.beforeEach(async (to, from, next) => { if (!userStore.hasRoute) { const menus = await getMenus(); menus.forEach(menu => router.addRoute(menu)); userStore.hasRoute = true; next({ ...to, replace: true }); } else { next(); } });

如果懒得自己写,可以直接参考若依微服务 plus 前端,它对菜单权限、按钮权限、动态路由封装得比较完整。唯一要注意的是若依老版本是 Vue2,新版本有 Vue3 改造版,选型时看清楚依赖版本。

4.2 文件预览:图片、PDF 与 M3U8 播放

易物系统里用户会上传物品图片,偶尔还有说明书 PDF,后续可能支持视频。图片最简单,用<img :src>就行。但如果 MinIO Bucket 是私有权限,不能直接用这个 URL,需要后端生成带过期时间的预签名 URL,前端拿到后设置到 src。

PDF 在浏览器里默认不一定能直接预览。把对象存储的 PDF 链接扔到 iframe 里,有些浏览器会显示下载按钮或空白。我用的方案是 pdfjs-dist 或 vue-pdf-embed,把 PDF 文件二进制拉下来用 Canvas 渲染。这个体验更好,但要注意处理加载失败时的错误提示。

M3U8 视频这里多说一句:浏览器不会原生播放 m3u8,必须引入 hls.js。代码很简洁:

import Hls from 'hls.js'; if (Hls.isSupported()) { const hls = new Hls(); hls.loadSource(videoUrl); hls.attachMedia(videoElement); } else { videoElement.src = videoUrl; }

如果视频在 MinIO 里,CORS 配置不对会导致切片请求失败。需要在 MinIO 的 bucket 策略或 Nginx 层加上Access-Control-Allow-Origin。我在测试时经常遇到“hls.js: Error while trying to load resource”的报错,十有八九是浏览器跨域被拦,先在控制台看 Network 里的响应头。

4.3 axios 封装与网关路由配置要点

前后端联调最麻烦的是鉴权和网关路由。我封装了一个 request.js 工具,请求拦截器里带上 token:

service.interceptors.request.use(config => { config.headers.Authorization = `Bearer ${getToken()}`; return config; });

响应拦截器统一处理错误码,比如 401 跳登录页,403 提示无权限,500 弹业务错误。不要把后端返回的code和 HTTP status 混在一起,我习惯后端所有业务错误都返回 HTTP 200,业务码放在 response body 的 code 字段里,前端只处理业务 code。这样网关的 HTTP 状态码只反映网络层面问题,不会出现“业务失败但 HTTP 200”和“业务成功但 HTTP 500”的混乱。

开发环境的跨域交给 Vite 代理:

server: { proxy: { '/api': { target: 'http://localhost:8888', changeOrigin: true } } }

生产环境用 Nginx 把/api转发到网关。网关路由配置里我用 StripPrefix=1,意思是从/api中把前缀剥掉,再转发到具体服务。比如前端请求/api/item/list,网关先剥离/api,再按 pathitem/list匹配到 item-service。如果 StripPrefix 设置不对,服务端看到的是/api/item/list,而服务里的接口是/item/list,就会报 404。排查这种问题,先看网关日志中的原始路径和转发路径,几乎一眼定位。

5. 上线过程中遇到的常见问题与排查实录

5.1 版本兼容性:SpringBoot 版本太高/SpringCloud Alibaba 版本不匹配

这是微服务项目里最折磨人的问题。我最初把 SpringBoot 升到 3.x,结果 SpringCloud Alibaba 里很多组件跟不上,启动报一堆 Bean 找不到或方法签名错误的异常。后来老老实实锁定版本:SpringBoot 2.7.x,SpringCloud 2021.0.x,SpringCloud Alibaba 2021.0.5.0。这个组合经过大量开源项目验证,稳定很多。

如果你用的不是 SpringCloud Alibaba,而是其他分布式方案,也是一样的原则:先确认 BOM 依赖,而不是网上随便拉一个最新版。SpringCloud 的版本号是伦敦地铁站名,比如 2021.0.x、2022.0.x,不写版本号可能会拉到不相容的版本。建议用一个父 pom 统一管理:

<dependencyManagement> <dependencies> <dependency> <groupId>com.alibaba.cloud</groupId> <artifactId>spring-cloud-alibaba-dependencies</artifactId> <version>2021.0.5.0</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>

项目启动后如果日志里提示 Nacos 客户端连不上,先别急着改代码,检查 Nacos 的 namespace 和 group 是否一致。我有一次所有服务都注册不上,查了半天,发现是 Nacos 2.x 开启了鉴权,但服务配置里没写 username/password,控制台看得见,连接却鉴权失败。控制台可见不代表配置没问题,要在服务端日志里找真正的报错。

5.2 分布式锁和事务的本地复现技巧

本地调试多实例有一个简单方法:同一个服务启动两个端口,比如--server.port=9003和--server.port=19003,都注册到同一个 Nacos 服务名。这样能模拟两个实例并发调用接口。测试重复交换时,我写了一个简单的多线程脚本,用同一个 token 并发发送 20 次申请。没有锁之前,数据库里会留下多条状态乱七八糟的记录。加了锁之后,日志里能看到只有一个请求进入业务区,其余线程都等锁或直接拒绝。

测试 Seata 回滚,最直接的方式是在确认交换的接口里故意抛一个运行时异常,然后看数据库。如果 Seata 配置正确,物品状态会从 HOLD 恢复成 AVAILABLE,exchange_apply 也会被回滚删除。如果发现没有回滚,先检查 undo_log 表有没有记录,再看 Seata Server 的日志。这里有个坑:Seata 版本和 SpringBoot 版本不匹配,会导致全局事务不生效,但业务代码不报错。所以上线前一定要做一次真实的“异常注入测试”,比看一百篇文档都管用。

5.3 缓存与 DB 一致性、慢查询优化

上线后物品列表慢慢变慢,我这里出现过两个问题。一个是没有索引的大表全表扫描。goods 表数据量到几十万后,where status='AVAILABLE' order by created_at desc就需要几百毫秒。解决办法是加联合索引(status, created_at),查询时间降到个位数毫秒。另一个是列表页连表查图片和用户信息,一条 SQL join 太多。我把物品列表的图片改成冗余存储,在 item 表里直接存主图 URL,避免每行都去 item_image 表查。

缓存更新顺序我采用“先更新数据库,再删除缓存”。这个方案比先删缓存再更新数据库要稳,因为并发期间可能出现短暂旧数据,但下次请求时缓存已删除,会回源数据库缓存新值。偶尔还是会出现删除缓存失败,所以加了一个延迟双删策略:更新数据库后删除一次缓存,等 500 毫秒后再删除一次,用来兜底并发读请求可能重建的旧缓存。

5.4 部署与运维

整个系统用 Docker Compose 编排。Nacos、Redis、RabbitMQ、MySQL、MinIO 各自一个容器,六个业务服务也各自打包成镜像。数据库连接串、Nacos 地址、Redis 地址都放到环境变量,不要写死在 application.yml,否则换环境就要重新打包。

网络规划上,所有服务放在一个 Docker 网络里,服务名直接写容器名。比如 user-service 的 datasource url 是jdbc:mysql://mysql:3306/user_db,而不是 localhost。在 Nacos 里注册时,IP 要配置为局域网或容器 IP,服务间 Feign 调用才能走通。很多人本地跑得好好的,放到 Docker Compose 里就注册不上,就是因为 localhost 指的不是容器本身,而是服务所在容器,造成网络隔离。

网关是唯一对前端暴露的入口,生产环境我用 Nginx 做了一层安全代理,把 TLS 终结在 Nginx,再转发到网关 8888。这样网关不需要处理证书,业务服务也不直接对外暴露端口,安全性好得多。

最后说点实际的体会。这套系统我前后折腾了一个多月,最大的感受是:微服务不是银弹,易物系统的业务难点在信任机制和状态一致性,而不在于用了多少新技术。用户最关心的不是你的服务拆了几个,而是“我发起交换之后,到底能不能真的换到东西”“有没有人会骗我”。如果你只是想学习分布式技术,这套 SpringBoot + Vue + SpringCloud 的组合很值得完整做一遍,能从网关、注册中心、锁、事务、缓存、文件存储一路练过去;但如果只是为了快速验证业务,单体加 Redis 完全够用,不要一开始就套微服务。先把核心状态机和幂等设计理清楚,再考虑拆分,才不会把自己绕进去。

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

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

立即咨询