ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

SpringCloud电商平台源码拆解:微服务架构与AI推荐落地实践

SpringCloud电商平台源码拆解:微服务架构与AI推荐落地实践 简介微服务架构是当前分布式系统设计的核心思想它将单体应用拆分为多个独立部署的服务通过轻量级通信机制协作解决业务复杂度高、迭代效率低、扩展性受限等工程痛点。SpringCloud作为微服务生态的主流技术栈通过注册中心、网关、配置中心等组件为服务治理提供了标准化的基础设施。电商平台是微服务架构的典型应用场景涉及商品、订单、用户、支付等核心业务模块天然适合按领域边界拆分服务。同时人工智能技术正在重塑电商体验基于用户行为的商品推荐和基于语义理解的智能检索成为提升转化率的关键能力。本文从一份完整的SpringCloud电商平台源码出发深入拆解其多模块工程结构、Nacos服务注册与发现链路、Spring Cloud Gateway网关路由规则以及基于协同过滤的AI推荐服务和基于Elasticsearch的智能搜索实现。通过梳理启动顺序、接口调用链和缓存降级策略帮助开发者快速掌握从单体到微服务改造的完整路径并为课程设计或生产项目提供可直接落地的参考范例。1. 先拆开看SpringCloud 电商平台这套源码解决的是从单体到微服务的那道坎做电商平台后端开发的人迟早会遇到一件事手里的单体项目越改越乱订单、商品、用户全塞在一个工程里改一个登录逻辑就得把整个服务重新打包。这份基于 SpringCloud 的电商平台源码解决的就是这个问题——它把商品、订单、用户、推荐、搜索拆成独立微服务用注册中心、网关、配置中心把它们串成一条完整链路人工智能部分则落在商品推荐和智能检索上不是挂个名字而已。对正在学微服务、准备做课程设计或想把手头单体商城项目改造成微服务的人来说这份 zip 解压后的多模块工程比看一百篇概念文章都直观。你会看到真实的 Feign 调用、网关路由、Nacos 配置、服务间鉴权以及推荐接口从数据库行为表到结果返回的完整代码。下面我按拆包、启动、接线、排错、改造的顺序把这套源码从头到尾过一遍。2. 组件选型与模块拆解注册中心、网关、AI 服务各自扮演什么角色2.1 为什么是 SpringCloud电商场景下的选型理由先回答一个很多人问过的问题电商平台做微服务为什么常见教材和课程设计都选 SpringCloud而不是 Dubbo 或者直接上 Kubernetes我的理解是——SpringCloud 全家桶对业务开发者的侵入感最低。你不需要先懂容器编排也不需要手动维护服务目录只要把依赖加进 pom注解一标服务就能注册、能被发现、能被网关转发。这套源码走的也是标准 SpringCloud 路线。注册中心用的是 Nacos网关是 Spring Cloud Gateway服务间调用走 OpenFeign配置集中放在 Nacos Config熔断降级接入 Sentinel。这套组合在中小型电商项目里出镜率最高原因是它们都有对应的控制台界面出问题了能直接看到服务实例、调用链和限流日志对新手友好对调试也友好。那人工智能模块放在哪里这个很关键——它不是独立部署一个大模型服务而是以两个业务接口的形态嵌在商品服务和搜索服务里。商品推荐接口读用户行为表基于协同过滤思路算相似商品搜索接口做分词和联想词补全。这种落地方式的好处是不动整体架构AI 作为一个可替换的业务模块存在后面你想换成自己的模型只需要改一个 Service 实现。2.2 源码工程结构一个 zip 解压出来是什么样解压这份源码后你会看到一个多模块 Maven 工程顶层目录大致是这样ecommerce-ai-platform/ ├── pom.xml # 父工程统一管理依赖版本 ├── ecommerce-gateway/ # 网关服务端口 8080 ├── ecommerce-auth/ # 认证服务端口 8100 ├── ecommerce-user/ # 用户服务端口 8101 ├── ecommerce-product/ # 商品服务端口 8102 ├── ecommerce-order/ # 订单服务端口 8103 ├── ecommerce-recommend/ # 推荐服务AI端口 8104 ├── ecommerce-search/ # 搜索服务AI端口 8105 └── sql/ # 初始化脚本建库建表 ├── init_user.sql ├── init_product.sql └── init_order.sql每个子模块都是独立的 Spring Boot 应用通过父 pom 统一管理版本。你不需要手动一个个导入用 IDEA 打开顶层 pom.xml选择“Open as Project”Maven 会自动把子模块识别出来。各服务模块的角色定位如下模块端口职责关键依赖gateway8080统一入口路由转发、跨域处理gateway, nacos discoveryauth8100登录、token 签发与校验security, jjwtuser8101用户信息 CRUDmybatis-plus, mysqlproduct8102商品分类、商品详情mybatis-plus, mysqlorder8103订单创建、订单查询mybatis-plus, seatarecommend8104商品推荐AI 协同过滤redis, mysqlsearch8105商品搜索AI 分词联想elasticsearch这里要提醒一句如果你看到自己的解压目录里少了某个模块先别急着怀疑资源不完整去检查sql目录和doc目录。有些教学版源码会把搜索服务简化成打包好的 jar不放在源码目录里但 SQL 脚本一定在。2.3 服务间调用链路从下单到推荐的完整流程这套源码里最具参考价值的是它把业务链路打通了。用户浏览商品、下单、支付后订单服务会通过 OpenFeign 调用推荐服务把“用户最近购买的商品 ID 列表”传过去推荐服务再基于这个列表计算相似商品。举个例子订单服务里会有一段这样的调用代码Component public class RecommendFeignClient { Autowired private RecommendService recommendService; /** * 用户支付完成后触发推荐刷新 * * param userId 用户 ID * param productIds 本次购买的 SKU 列表 */ public void refreshAfterOrder(Long userId, ListLong productIds) { // 同步调用推荐服务传用户ID和商品ID集合 // 推荐服务内部会更新 Redis 中的用户偏好集合 RecommendRequest request new RecommendRequest(); request.setUserId(userId); request.setProductIds(productIds); // 这里走 Feign超时时间在配置中心里设置 recommendService.refreshUserPreference(request); } }这段代码的逻辑很简单用户在订单服务完成支付后异步或同步地告诉推荐服务“这个用户买了什么东西”推荐服务拿到数据后更新 Redis 里的用户行为集合。下次用户再请求首页推荐位推荐接口直接查 Redis而不是临时跑一遍计算。参数说明userId是用户主键productIds是订单关联的商品 ID 列表。如果商品数量大建议把refreshAfterOrder方法改成用 MQ 做异步通知避免支付接口被推荐刷新拖慢。这套源码里没有引 MQ算是给学生版做的简化实际生产环境一般会加 RocketMQ 或 RabbitMQ 解耦。2.4 数据库设计五张核心表是怎么支撑 AI 推荐的推荐模块能跑起来依赖三张核心表user用户表、product商品表、order_item订单明细表。AI 推荐的计算逻辑是从order_item里统计“买了 A 商品的用户还买了 B 商品”这个统计结果会定时生成一张相似度表product_similarity。-- 商品相似度表用于协同过滤推荐 CREATE TABLE product_similarity ( id bigint(20) NOT NULL AUTO_INCREMENT, product_id bigint(20) NOT NULL COMMENT 商品A, similar_product_id bigint(20) NOT NULL COMMENT 商品B, score decimal(6,4) NOT NULL COMMENT 相似度评分 0~1, update_time datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), UNIQUE KEY uk_product_similar (product_id, similar_product_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4 COMMENT商品相似度表;这张表是推荐服务的“饲料”。启动后会有一个SimilarityJob定时任务每天凌晨跑一次扫描order_item里所有用户的历史购买记录用余弦相似度计算商品之间的关联度把结果写入product_similarity。用户请求推荐时推荐服务直接查这个表按score降序返回 Top-N 商品。这里有个关键点定时任务不是用 xxl-job 这类分布式调度框架而是用 Spring 自带的Scheduled注解实现的。在单机部署下没问题如果你后面要部署多副本这个任务会重复执行到时候需要加分布式锁或改用 xxl-job。源码注释里应该也标了这一点。3. 从 zip 到跑通环境匹配、建库建表与五步启动顺序3.1 环境版本匹配先对号入座再动手拆开源码后先别急着运行把环境版本对好能省掉后面一大半的兼容性问题。以教学版 SpringCloud 源码的常见搭配来说JDK 1.8 Spring Boot 2.3.x Spring Cloud Hoxton.SR12 是一套被验证过无数次的组合Nacos 用 1.4.x 版本最稳妥Sentinel 控制台用 1.8.x 也可以正常接上。如果你电脑里装的是 JDK 17 甚至更高又没改过 pom.xml大概率会在编译阶段收到cannot find symbol或package javax.servlet does not exist的报错。这不是源码有问题是 Lombok 和旧版 Spring Boot 对新 JDK 的支持没跟上。组件建议版本备注JDK1.8兼容性最好遇到问题最少Maven3.6.33.8 以上需要检查镜像源MySQL5.7 或 8.08.0 需要改 driver 配置Redis5.0推荐服务缓存用Nacos1.4.x2.x 需要额外注意鉴权配置Elasticsearch7.x搜索服务依赖如果你手里的源码用的是 Spring Boot 2.4 以上版本那么配置文件从application.yml变成了bootstrap.yml优先加载访问spring.cloud.nacos.config的写法也要对应调整。先看一眼父 pom 里的版本号再决定用哪个 JDK这个顺序不能反翻车重来很浪费时间。3.2 配置中心与服务注册Nacos 初始化第一步不是启动 MySQL而是先把 Nacos 跑起来。推荐服务、商品服务、网关服务启动时都要向 Nacos 注册自己Nacos 起不来所有服务都会报连接超时。# 单机模式启动 Nacos端口 8848 cd nacos/bin startup.cmd -m standalone # Windows 环境 # ./startup.sh -m standalone # Linux / Mac 环境启动后访问http://localhost:8848/nacos默认账号密码是nacos/nacos。你需要在控制台创建一个命名空间命名空间 ID 填ecommerce-dev因为源码里的配置会引用这个 ID。如果不创建命名空间或者 ID 对不上服务启动时会一直重试拉取配置日志里会出现config data not found的提示。接下来把sql/目录下的三个 SQL 脚本按文件名顺序导入数据库。先导init_user.sql再导init_product.sql最后导init_order.sql顺序倒过来会因为外键约束报错。mysql -uroot -p --default-character-setutf8mb4 init_product.sql这里必须加--default-character-setutf8mb4否则商品表里的中文商品名会被写成乱码。这个坑我踩过不止一次后面避坑章节会展开说。3.3 修改配置文件数据库连接与 Redis 地址服务模块下有src/main/resources/目录里面都有对应的application.yml。需要修改的是数据库密码、Redis 密码和 Nacos 地址。以商品服务为例server: port: 8102 spring: application: name: ecommerce-product cloud: nacos: discovery: server-addr: 127.0.0.1:8848 config: server-addr: 127.0.0.1:8848 namespace: ecommerce-dev file-extension: yaml datasource: url: jdbc:mysql://127.0.0.1:3306/ecommerce_product?useUnicodetruecharacterEncodingutf8serverTimezoneAsia/Shanghai username: root password: your_password driver-class-name: com.mysql.cj.jdbc.Driver redis: host: 127.0.0.1 port: 6379 password: your_redis_password配置说明server.timezoneAsia/Shanghai千万不要省略MySQL 8.0 默认时区是 UTC不改成上海时间的话订单表里的create_time会和本地时间差 8 小时排查问题的时候会怀疑人生。driver-class-name如果源码用的是com.mysql.jdbc.Driver而你的 MySQL 是 8.0编译器会提示已弃用建议直接改成com.mysql.cj.jdbc.Driver两个都能跑但新版写法更稳。Redis 密码如果不为空所有连 Redis 的模块都要改包括推荐服务和搜索服务。网关和认证服务不直接连 Redis但认证服务校验 token 时可能会通过 Feign 调用户服务用户服务连不上 Redis 就会引发级联失败。3.4 启动顺序先基础设施再业务服务这套源码启动顺序有讲究不是随便挨个mvn spring-boot:run就能全起来的。正确顺序是启动 Nacos前面已完成。启动 MySQL 和 Redis确认端口被监听。启动认证服务ecommerce-auth。启动用户、商品、订单三个基础业务服务。启动推荐服务和搜索服务AI 模块。最后启动网关服务。网关为什么最后启动因为它启动时要拉取所有服务的路由注册信息如果前面的服务还没注册上来网关会出现短暂的路由缺失虽然 Nacos 会在后续自动同步但是第一次启动时经常看到No route found的日志容易吓到自己。每个模块单独启动的方式是一样的cd ecommerce-auth mvn spring-boot:run如果你用的是 IDEA也可以直接在AuthApplication.java上右键 Run。启动成功的标志是控制台出现Registered instance with nacos日志说明这个服务已经注册到 Nacos 了。所有服务都起来后打开 Nacos 控制台的服务列表页面应该能看到 6 个服务都是健康状态这时候再去调网关接口就不该报连接拒绝了。3.5 接口验证一条命令确认链路通不通服务全部启动后先别急着打开前端页面用 curl 验证一下链路是最快的。先走认证服务拿 token再带 token 调商品接口和推荐接口。# 第一步登录获取 token curl -X POST http://localhost:8080/api/auth/login \ -H Content-Type: application/json \ -d {username:admin,password:123456} # 第二步携带 token 查询商品列表 curl http://localhost:8080/api/product/list \ -H Authorization: Bearer 上一步返回的token # 第三步调用推荐接口 curl http://localhost:8080/api/recommend/home?userId1 \ -H Authorization: Bearer 上一步返回的token三个接口都返回正常 JSON 而不是 401 或 503说明网关转发、服务注册、Feign 调用、MyBatis 查询整条链路都是通的。如果第二步返回 401先检查认证服务生成的 token 里包含的角色权限和网关的路由鉴权配置是否匹配。如果第三步返回 500直接去推荐服务的控制台看异常堆栈十有八九是 Redis 缓存没命中后查 MySQL 时表名找不到。4. 人工智能落地点商品推荐与智能检索的两个真实接口4.1 推荐服务接口从用户行为表到 Top-N 商品推荐模块是这份源码里“人工智能”含量最高的地方。它的实现逻辑不复杂但很完整走的是经典协同过滤路线。核心入口是RecommendServiceImpl里的recommendForUser方法Service public class RecommendServiceImpl implements RecommendService { Autowired private ProductSimilarityMapper similarityMapper; Autowired private RedisTemplateString, Object redisTemplate; private static final String REDIS_KEY_PREFIX recommend:user:; Override public ListProductVO recommendForUser(Long userId, int size) { // 1. 先从 Redis 查缓存缓存命中直接返回 String redisKey REDIS_KEY_PREFIX userId; ListProductVO cached (ListProductVO) redisTemplate.opsForValue().get(redisKey); if (cached ! null !cached.isEmpty()) { return cached.subList(0, Math.min(size, cached.size())); } // 2. 缓存未命中查用户最近购买的商品 ID ListLong boughtProductIds orderItemMapper.findProductIdsByUserId(userId); // 3. 用购买过的商品去 product_similarity 表找关联商品 ListProductVO recommended similarityMapper.findSimilarProducts( boughtProductIds, size * 2 // 多取一批过滤掉已购买商品后取前 size 个 ); // 4. 过滤掉用户已经买过的商品 recommended.removeIf(item - boughtProductIds.contains(item.getId())); // 5. 写入 Redis 缓存过期时间 1 小时 redisTemplate.opsForValue().set(redisKey, recommended, 1, TimeUnit.HOURS); return recommended.subList(0, Math.min(size, recommended.size())); } }这段代码的逻辑分五步每一步都有明确的工程考量。第一步查缓存是为了挡高并发推荐接口是首页高频调用每次请求都去 MySQL 跑一次相似度 SQL 的话商品服务扛不住五分钟。第五步设置一小时过期时间是让用户的行为变化能在一小时后反映到推荐结果里如果设成永久缓存用户买了新商品后推荐结果不会更新。参数说明size是前端要求的推荐数量常见值是 10 或 20size * 2是为了多查一批候选商品过滤已购买项后保底。你可以把 Redis 过期时间改成 24 小时但建议不要把size * 2改成size不然过滤完可能不足size条。这套逻辑对新手理解 AI 推荐的工程落地很有帮助。它没有训练模型、没有特征工程但作为课程设计或生产项目的 MVP 版本该有的缓存策略、降级策略、过滤策略都有了。如果你想把它升级成真正的协同过滤算法替换findSimilarProducts的实现即可。4.2 智能检索接口分词、联想词与搜索历史记录搜索模块的 AI 感体现在两个地方一是商品名称分词检索二是用户输入前缀时的联想词补全。这两个能力都是基于 Elasticsearch 实现的源码里对应的操作封装在SearchServiceImplService public class SearchServiceImpl implements SearchService { Autowired private RestHighLevelClient esClient; Override public ListString suggestKeywords(String prefix, int limit) { // 构建前缀补全查询只查商品名称字段 CompletionSuggestionBuilder suggestionBuilder new CompletionSuggestionBuilder(name_suggest) .prefix(prefix) .size(limit); SearchSourceBuilder sourceBuilder new SearchSourceBuilder(); sourceBuilder.suggest(new SuggestBuilder().addSuggestion(suggest, suggestionBuilder)); // 执行查询并解析联想词 // 常见做法是取第一个 suggestion 的 options 列表 return parseSuggestOptions(esClient.search(...)); } }这里的name_suggest字段用的是 Elasticsearch 的 completion 类型底层数据结构是 FST有限状态转移机专门做前缀联想性能比普通 match 查询高一个量级。电商搜索框的“实时联想”基本都这么做。分词功能则由 IK 分词器插件负责商品名称为“华为 Mate 60 Pro 手机”时会被切分成“华为 / mate / pro / 手机”等词元用户搜“华为手机”也能命中。源码里有一个ElasticsearchIndexInitializer类负责在服务启动时自动创建索引和 mapping你不用手动执行PUT /product命令。唯一要做的是保证 Elasticsearch 7.x 版本和 IK 插件版本对应IK 装错版本会直接导致搜索服务启动失败。4.3 AI 服务的降级逻辑缓存挂了怎么办线上系统最忌讳“AI 服务挂了整个页面都打不开”。这套源码里做了一个很实用的兜底策略推荐接口查 Redis 失败时不直接抛异常而是降级查 MySQL 热门商品表把点赞数最高的 10 个商品返回给前端。// 降级方法定义参数和原方法保持一致 Degrade public ListProductVO fallbackRecommend(Long userId, int size, Throwable e) { // 查热销商品表按销量倒序取前 size 条 return productMapper.findHotProducts(size); }这个Degrade注解是 Sentinel 的降级注解源码里配合 Sentinel 控制台使用。触发降级后控制台会记录降级次数你可以配置阈值规则5 秒内异常数超过 5 次就自动降级恢复需要 10 秒。这样就保证了推荐服务就算 Redis 崩溃或者相似度表数据没生成用户依然能看到商品列表只是推荐精准度变差了而已。5. 启动避坑与排查端口、时区、跨域与注册失败的四处翻车现场5.1 Nacos 启动闪退startup.cmd一闪而过现象双击startup.cmd后黑窗口闪了一下就没了控制台没有任何报错信息。原因Nacos 默认以集群模式启动单机环境没有配置集群节点启动脚本直接退出。另外 JDK 环境变量没配好也会导致闪退Windows 上尤其常见。解决先检查JAVA_HOME是否指向 JDK 1.8确认后在 cmd 里手动执行startup.cmd -m standalone这样即使启动失败窗口也会保留错误信息。常见错误是找不到JAVA_HOME或者 Nacos 2.x 版本对 JDK 版本有额外要求换回 1.4.x 版本基本能解决。提示别用老版本的 Nacos 1.3 跑这套源码配置语法不完全兼容会出现dataId not found的警告虽然不阻塞启动但配置拉不全会导致接口报错。5.2 MySQL 中文乱码商品名全是问号现象商品列表接口返回的数据里中文商品名显示为???。原因SQL 脚本导入时没有显式指定字符集或者数据库本身创建时不是 utf8mb4。MySQL 8.0 默认字符集是 utf8mb4但 5.7 的默认字符集可能还是 latin1一旦表结构建错后面改起来非常麻烦。解决删除数据库重建导入前执行SET NAMES utf8mb4;并确保 URL 参数里带了characterEncodingutf8。检查表结构的字符集SELECT TABLE_NAME, TABLE_COLLATION FROM information_schema.TABLES WHERE TABLE_SCHEMA ecommerce_product;如果看到latin1开头的排序规则说明表建错了必须重新建库。这个字段必须全部是utf8mb4_general_ci或utf8mb4_unicode_ci。5.3 网关路由报 503Service Unavailable 问题现象前端页面能打开但调用登录接口、商品接口时网关返回 503。原因服务已经启动了但网关启动时这些服务还没注册到 Nacos或者 Feign 客户端懒加载没有触发重试。还有一种情况是服务注册到 Nacos 了但是网关的路由配置里的lb://服务名写错了比如商品服务注册名是ecommerce-product配置里写成了product-service服务名对不上就找不到实例。解决先打开 Nacos 控制台查看“服务管理-服务列表”确认所有服务实例是否为健康状态。再核对网关配置文件里的服务名spring: cloud: gateway: routes: - id: product-route uri: lb://ecommerce-product predicates: - Path/api/product/**lb://后面必须和spring.application.name完全一致大小写也要一致。如果确认都对重启网关后等待 10 秒再试服务注册有延迟刚启动完立刻调接口偶尔会遇到实例信息还没同步的情况。提示504 和 503 不一样504 是网关超时通常是 Feign 调用业务服务时对方处理太慢需要调大ribbon.ReadTimeout而不是排查路由。5.4 跨域报错前端联调时 Access-Control-Allow-Origin 缺失现象用前端项目特别是小程序 web-view 或本地 Vue 开发服务器调用网关接口时浏览器控制台报跨域错误请求根本发不出去。原因网关默认不开启跨域配置。这套源码的 gateway 模块里需要设置全局 CORS但很多课程设计模板漏了这一层或者只在某个 Controller 上加CrossOrigin结果只有那一个接口能跨域其他接口全部被浏览器拦截。解决在网关模块加一个全局 CORS 配置类代码如下Configuration public class CorsConfig { Bean public CorsWebFilter corsWebFilter() { CorsConfiguration config new CorsConfiguration(); config.addAllowedOrigin(*); config.addAllowedMethod(*); config.addAllowedHeader(*); config.setAllowCredentials(true); UrlBasedCorsConfigurationSource source new UrlBasedCorsConfigurationSource(); source.registerCorsConfiguration(/**, config); return new CorsWebFilter(source); } }注意第 7 行setAllowCredentials(true)和addAllowedOrigin(*)不能同时使用否则浏览器会报“credentials mode 不兼容”。前端不带 cookie 的话把setAllowCredentials注释掉即可。小程序端不受浏览器跨域限制但如果是 H5 端这一步不做跑不通。提示小程序和网页共用这套源码时建议网关只保留无状态鉴权token 放请求头传别依赖 cookie小程序请求压根不带 cookie。5.5 推荐服务启动成功但接口 500ES 索引初始化失败现象推荐服务启动没有报错但调/api/recommend/home时返回 500日志提示index_not_found_exception。原因Elasticsearch 的索引初始化器在服务启动时执行但如果 ES 连接串配置错误或者索引已经存在但 mapping 版本不匹配初始化器可能静默跳过。商品数据没有导入 ES查询时自然找不到索引。解决确认application.yml里的 ES 地址正确后手动执行初始化接口这套源码一般会在搜索服务里提供一个POST /api/search/reindex接口用于重新构建索引。调用后去 Kibana 或 ES 的_cat/indices接口确认索引存在curl http://127.0.0.1:9200/_cat/indices?v看到ecommerce_product索引文档数大于 0再回头调推荐接口就正常了。6. 进阶把推荐算法换成你自己的数据再给网关做一次压测当你把服务全部跑通发现推荐接口返回的永远是同一批商品时就该动真格的了。我建议你做三件事第一把推荐服务的数据源换成你自己的订单数据第二把相似度表改成定时任务刷新第三拿 JMeter 对网关接口做一个基础压测看看这套源码的吞吐量边界在哪。先说换数据源。最简单的方式是往order_item表里多插几行模拟数据覆盖不同用户的购买行为重叠。比如用户 A 买了商品 1、2、3用户 B 买了商品 2、3、4那么商品 2 和商品 3 的相似度就应该升高。你可以用 Navicat 直接导几行数据然后去推荐服务里手动触发一次SimilarityJob.refresh()再调推荐接口看结果是否变化。如果变化了说明协同过滤链路完全跑通如果没变化检查定时任务是否被Scheduled正常加载以及在推荐服务所在模块的启动类上是否加了EnableScheduling注解。SpringBootApplication EnableScheduling public class RecommendApplication { public static void main(String[] args) { SpringApplication.run(RecommendApplication.class, args); } }这里有个容易被忽视的细节product_similarity表里的score字段是DECIMAL(6,4)最大只能存 99.9999余弦相似度的结果在 0 到 1 之间所以这个字段不会溢出。但如果你把算法改成皮尔逊相关系数结果范围是 -1 到 1DECIMAL(6,4)照样够用。真正要注意的是定时任务执行时间——默认是凌晨 2 点你手动验证时等不到那个点所以要么改 cron 表达式要么直接在测试类里调用一次refresh()方法。第二件事关于缓存策略的优化思路。默认的 Redis 缓存过期时间是一小时如果你的商品更新频繁一小时内的推荐结果可能滞后。我习惯把热门商品的推荐缓存改成 10 分钟把冷门商品改成 2 小时。实现方式不复杂在写入 Redis 时按商品类目判断long ttl product.getCategoryId() 1 ? 10 : 120; redisTemplate.opsForValue().set(redisKey, recommended, ttl, TimeUnit.MINUTES);这种按业务维度区分缓存时间的做法算是对推荐系统的一种实用调优比一刀切设一个固定 TTL 效果要好。第三件事是压测。用 JMeter 创建一个线程组50 并发对http://localhost:8080/api/recommend/home?userId1发起 GET 请求观察吞吐量。没有配 Sentinel 限流的话这台机器上推荐服务的 QPS 应该能到几百。如果你发现吞吐量异常低先看是不是 Redis 连接池配置太小再看网关线程数。推荐服务的spring.redis.lettuce.pool.max-active默认是 850 并发时这个连接池会被瞬间打满续约不够快就会产生等待压测结果自然难看。spring: redis: lettuce: pool: max-active: 32 max-idle: 16 min-idle: 8 max-wait: 3000ms把这些参数填进去重启推荐服务再压一次吞吐量通常能翻一倍。从那以后我每拿到一套 SpringCloud 电商源码都会先做三件事解压后查版本、按顺序启动服务、用 curl 把关键链路走通然后再去动代码。这个顺序听起来枯燥但真的能帮你省下两小时的定位时间。希望这份 SpringCloud 电商平台源码也能成为你第一条跑通的微服务完整链路。本文还有配套的精品资源点击获取
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进