Vue + SpringCloud 微服务博客实战:从单体拆分到网关鉴权与缓存一致性

发布时间:2026/10/9 12:32:18
Vue + SpringCloud 微服务博客实战:从单体拆分到网关鉴权与缓存一致性 简介这是一套基于Vue与SpringCloud的前后端分离博客系统完整源码面向具备Java与前端基础、希望深入微服务架构与分布式部署的开发者可用于课程设计、毕业设计或全栈项目实战参考。压缩包共1025个文件约91.44MB以360个js、265个java、70个vue为核心辅以xml、json、yml等配置与依赖文件另有scss、less样式及图片、文档等资源结构完整。项目采用高可用Eureka与Zuul结合Feign实现负载均衡并配置回退避免级联故障同时以Elasticsearch作为Zipkin存储跟踪微服务Api指标。技术栈覆盖Mybatis分页、Redis缓存、Redisson分布式锁、RabbitMQ延迟队列、WebSocket实时通信及支付宝支付前端使用Vue、axios、UI组件与v-charts图表代码注释齐全、扩展方便最终以Docker方式部署。目前已有648人学习适合系统掌握微服务全栈开发与中间件整合的读者。1. 从单体到微服务一套博客系统为什么要拆成 Vue SpringCloud很多开发者第一次听到「基于 Vue SpringCloud 博客的设计与实现」第一反应是一个博客而已用得着上微服务吗我一开始也这么想。直到某次帮一个技术社区做内容平台重构单体应用在文章发布高峰期把数据库连接池打满首页接口响应从 200ms 飙到 4s运维重启了三次才扛过去。那次之后我才真正理解博客系统一旦涉及用户、文章、评论、搜索、文件、通知这些模块单体架构的耦合就会变成运维的噩梦。这套方案的核心思路是前端用 Vue 做组件化渲染和路由管理后端用 SpringCloud 把博客拆成用户服务、文章服务、评论服务、搜索服务、文件服务等独立单元通过注册中心、网关、配置中心把它们串起来。它适合有一定 Java 和 Vue 基础、想从单体 CRUD 过渡到微服务架构的开发者也适合需要支撑多端内容分发的团队。接下来我会把选型理由、搭建步骤、参数配置和踩过的坑一条条讲清楚。2. 服务拆分与注册发现Nacos 怎么配才不翻车2.1 博客微服务的拆分粒度怎么定拆分的第一个问题是粒度。拆得太粗等于没拆拆得太细一个博客搞出十几个服务运维成本反而更高。我一般按「业务边界 数据独立性」来切博客场景下推荐拆成五个核心服务服务名职责独立数据表user-service注册、登录、JWT 签发、用户信息t_userarticle-service文章 CRUD、分类标签、草稿t_article、t_categorycomment-service评论、回复、审核状态t_commentsearch-service全文检索、关键词高亮Elasticsearch 索引file-service封面图、附件上传下载对象存储网关和注册中心不承载业务数据单独部署。这样拆的好处是文章服务压力大时可以单独扩容评论服务出问题不会拖垮登录。注意不要按「controller 层一个服务、service 层一个服务」这种技术分层去拆那是典型的伪微服务后面调用链会乱成一团。2.2 Nacos 注册中心的配置与启动注册中心选 Nacos 而不是 Eureka主要原因是 Nacos 同时提供注册发现和配置管理少维护一个组件。下面是我常用的 Nacos 单机启动命令和 SpringCloud 服务端配置。# 启动 Nacos 单机模式指定独立存储避免重启丢配置 sh startup.sh -m standalone # 默认端口 8848控制台地址 http://localhost:8848/nacos# 每个业务服务的 bootstrap.yml 公共部分 spring: application: name: article-service # 服务名必须与调用方一致 cloud: nacos: discovery: server-addr: 127.0.0.1:8848 namespace: blog-dev # 多环境隔离生产用 blog-prod group: DEFAULT_GROUP config: server-addr: 127.0.0.1:8848 file-extension: yaml namespace: blog-dev逻辑说明spring.application.name是服务在注册中心里的唯一标识调用方通过这个名字做负载均衡。namespace用来隔离开发、测试、生产环境避免本地调试时把请求打到生产节点。file-extension决定配置中心拉取的文件格式要和 Nacos 里创建的配置类型一致。参数说明server-addr如果 Nacos 部署在另一台机器改成对应 IPgroup默认 DEFAULT_GROUP 即可除非你要做灰度分组。启动顺序上先起 Nacos再起各业务服务最后起网关。如果服务启动时报「no available server」九成是 Nacos 没起来或者 namespace 写错了。2.3 服务间调用的两种方式与选择服务间调用常见做法有两种RestTemplate LoadBalancer或者 OpenFeign。我一般用 OpenFeign因为声明式接口写起来更接近本地方法调用可读性好。// article-service 中声明调用 comment-service FeignClient(name comment-service, fallback CommentFallback.class) public interface CommentClient { GetMapping(/comment/count/{articleId}) ResultInteger countByArticleId(PathVariable(articleId) Long articleId); }逻辑说明FeignClient的name必须和 comment-service 注册到 Nacos 的名字完全一致大小写敏感。fallback指定降级类当评论服务不可用时返回兜底数据避免文章详情页整个挂掉。参数说明PathVariable里的值要和路径占位符一致否则运行时报参数绑定失败。Feign 默认超时较短建议在配置里把feign.client.config.default.connectTimeout设为 5000、readTimeout设为 10000否则评论服务稍慢就会触发熔断。3. 网关与鉴权JWT 在 SpringCloud 里怎么串起来3.1 网关路由配置与跨域处理网关是整个系统的入口所有前端请求先到网关再由网关转发到具体服务。Vue 前端开发时跑在 5173 或 8080 端口和后端不同源跨域必须在这里解决。spring: cloud: gateway: globalcors: cors-configurations: [/**]: allowedOriginPatterns: * # 生产环境替换为具体域名 allowedMethods: * allowedHeaders: * allowCredentials: true routes: - id: article_route uri: lb://article-service predicates: - Path/api/article/** filters: - StripPrefix1逻辑说明lb://表示走负载均衡到注册中心里的服务而不是写死 IP。StripPrefix1会去掉路径的第一段比如/api/article/list转发后变成/article/list这样后端 controller 不用带/api前缀。参数说明allowedOriginPatterns用通配符时不能和allowCredentials: true同时用allowedOrigins: *这是 Spring 的限制必须用 patterns。生产环境一定要把来源收窄到自己的域名否则等于把接口暴露给任何网站。3.2 JWT 鉴权过滤器与 Vue 端的配合鉴权放在网关做业务服务只信任网关传来的用户标识这样每个服务不用重复写登录校验。// 网关全局过滤器核心片段 public class AuthFilter implements GlobalFilter, Ordered { Override public MonoVoid filter(ServerWebExchange exchange, GatewayFilterChain chain) { String token exchange.getRequest().getHeaders().getFirst(Authorization); if (token null || !token.startsWith(Bearer )) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } try { Claims claims JwtUtil.parse(token.substring(7)); // 把用户 id 透传到下游服务 ServerHttpRequest req exchange.getRequest().mutate() .header(X-User-Id, claims.getSubject()).build(); return chain.filter(exchange.mutate().request(req).build()); } catch (Exception e) { exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED); return exchange.getResponse().setComplete(); } } Override public int getOrder() { return -100; } // 优先级高先执行 }逻辑说明过滤器从请求头取 token解析成功后把用户 id 塞进X-User-Id请求头传给下游。下游服务直接从请求头拿用户 id不再解析 token减少重复计算。参数说明getOrder返回 -100 表示在大多数内置过滤器之前执行。Vue 端用 axios 拦截器统一加 token// Vue 端 axios 请求拦截器 axios.interceptors.request.use(config { const token localStorage.getItem(token) if (token) config.headers.Authorization Bearer ${token} return config })注意 token 过期时间不要设太长博客系统一般 2 小时足够配合刷新 token 机制。如果直接把 token 存 localStorage要防 XSS富文本内容渲染时务必做转义。4. 数据一致性与缓存文章详情页的读写分离实践4.1 文章详情的多级缓存设计文章详情是读多写少的典型场景我一般用「本地缓存 Redis」两级。本地缓存扛热点文章Redis 扛全量数据库兜底。// article-service 中查询文章详情 public ArticleVO getArticleDetail(Long id) { String localKey article:local: id; ArticleVO vo localCache.getIfPresent(localKey); if (vo ! null) return vo; String redisKey article:detail: id; vo (ArticleVO) redisTemplate.opsForValue().get(redisKey); if (vo ! null) { localCache.put(localKey, vo); // 回填本地缓存 return vo; } vo articleMapper.selectDetailById(id); if (vo ! null) { redisTemplate.opsForValue().set(redisKey, vo, 30, TimeUnit.MINUTES); localCache.put(localKey, vo); } return vo; }逻辑说明先查本地缓存命中直接返回未命中查 Redis命中后回填本地再未命中查数据库写回两级缓存。本地缓存用 Caffeine设置最大条目和过期时间避免内存无限增长。参数说明Redis 过期时间设 30 分钟本地缓存设 5 分钟这样即使文章更新最坏情况 5 分钟后本地缓存失效不会长期脏读。更新文章时要主动删除 Redis key本地缓存靠短过期时间自然失效这是权衡后的做法。4.2 缓存与数据库的一致性处理更新文章时先更新数据库还是先删缓存是个老问题。我的做法是先更新数据库再删除 Redis 缓存本地缓存不主动删靠短过期兜底。Transactional public void updateArticle(ArticleDTO dto) { articleMapper.updateById(dto); // 删除 Redis 缓存下次查询重新加载 redisTemplate.delete(article:detail: dto.getId()); // 发送延迟消息二次删除防止并发读回填旧值 rocketMQTemplate.syncSend(article-cache-delete, dto.getId(), 1000); }逻辑说明先落库再删缓存配合延迟二次删除能覆盖大部分并发场景。延迟消息 1 秒后再删一次解决「读请求在删缓存后、更新前回填旧值」的窗口问题。参数说明延迟时间根据业务 QPS 调整一般 500ms 到 1s。如果没有消息队列可以用定时任务扫描变更表补偿但实时性差一些。注意不要用「先删缓存再更新数据库」那样并发下脏数据概率更高。5. 避坑与排查这套架构最容易翻车的五个地方5.1 服务启动报「No spring.config.import property has been defined」现象SpringCloud 2021 之后版本启动服务直接报错退出提示缺少 config import。原因新版 SpringCloud 要求显式声明配置中心导入方式不再自动读取 bootstrap.yml。解决在 application.yml 里加spring.config.import: optional:nacos:${spring.application.name}.yaml或者引入spring-cloud-starter-bootstrap依赖恢复旧行为。我一般用前者更符合新版本规范。5.2 Feign 调用超时导致文章详情页空白现象文章详情页偶尔空白日志里看到 Feign 超时异常但评论服务本身正常。原因Feign 默认连接超时 1 秒、读超时 1 秒评论服务在数据量大时响应超过 1 秒就触发熔断。解决在配置里调大超时并给 Feign 配重试。feign.client.config.default.connectTimeout: 5000、readTimeout: 10000同时确认熔断降级类返回了合理兜底数据而不是抛异常。5.3 Nacos 命名空间写错导致本地调不到测试服务现象本地启动服务后网关转发 404但服务在 Nacos 控制台能看到。原因本地服务注册到了 public 命名空间而网关配置的是 blog-dev 命名空间两者隔离互相发现不了。解决检查每个服务的spring.cloud.nacos.discovery.namespace是否一致。建议把 namespace 抽到公共配置里避免每个服务手写。本地调试时统一用同一个 namespace不要混用。5.4 Vue 打包后刷新页面 404现象开发环境正常打包部署后刷新非首页路由直接 404。原因Vue Router 默认 history 模式刷新时浏览器向服务器请求真实路径服务器没有对应资源。解决Nginx 配置try_files $uri $uri/ /index.html;把所有未匹配路径回退到 index.html。如果用 hash 模式则没这个问题但 URL 带 # 不好看。我一般用 history 模式加 Nginx 回退。5.5 文章内容里的 HTML 被转义或 XSS现象富文本编辑器保存的文章展示时标签被转义成文本或者反过来被注入脚本。原因前端渲染时用了错误的转义方式或者后端没有做白名单过滤。解决后端存储时保留原始 HTML输出时用白名单过滤如 Jsoup 的 Safelist前端用v-html渲染过滤后的内容。不要简单用textContent那样富文本全变纯文本也不要直接v-html未过滤内容那是 XSS 重灾区。6. 从能跑到好用接口压测与链路追踪的落地技巧系统跑起来只是第一步能不能扛住流量、出问题能不能快速定位才是微服务博客和单体博客的真正差距。我一般在上线前做两件事接口压测和链路追踪。压测用 JMeter 或 wrk重点压文章列表和详情两个接口。下面是我常用的 wrk 命令配合 Lua 脚本模拟带 token 的请求# 压测文章详情接口12 线程 400 连接持续 60 秒 wrk -t12 -c400 -d60s --latency \ -s auth.lua \ http://localhost:8080/api/article/detail/1-- auth.lua给每个请求加 JWT 头 wrk.method GET wrk.headers[Authorization] Bearer 你的测试token逻辑说明-t是线程数-c是连接数-d是持续时间--latency输出延迟分布。auth.lua负责注入鉴权头否则网关直接返回 401压测没意义。参数说明线程数不要超过 CPU 核数太多连接数根据目标 QPS 调整。看结果时重点关注 P99 延迟和错误率如果 P99 超过 500ms就要查是数据库慢查询还是缓存没命中。压测环境要和生产配置接近否则数据没参考价值。链路追踪用 SkyWalking 或 Sleuth Zipkin。我倾向 SkyWalking对代码零侵入探针挂上就能看调用链。部署时注意采样率生产环境设 10% 到 20% 即可全采样会拖慢服务。看链路时重点找耗时最长的 span通常是数据库查询或跨服务调用。最后一个习惯每次改完配置或加完服务先本地跑一遍完整链路——注册、登录、发文章、看详情、发评论、搜索六个动作走通再提交。微服务的坑大多不在单个服务里而在服务之间的连接处。我踩过最惨的一次是网关路由配错本地测试全过上线后所有请求 404回滚花了二十分钟。从那以后我坚持每次上线前用脚本跑一遍端到端冒烟测试这个习惯帮我省了无数次后悔药。希望帮到你。本文还有配套的精品资源点击获取

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询