ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Java旅游推荐系统:协同过滤+内容推荐+Swing GUI工程实践

Java旅游推荐系统:协同过滤+内容推荐+Swing GUI工程实践 简介本资源是一份面向Java中高级开发者的技术实践文档聚焦旅游行业个性化推荐场景提供从算法设计到GUI落地的完整项目实例。内容涵盖协同过滤与内容推荐双引擎实现、MySQL数据库建模、用户画像构建、景点管理与评价模块代码详解并深入探讨数据隐私保护、系统可扩展性及AR/VR等未来演进方向。资源为单个73KB的DOCX文档结构清晰含项目背景、挑战分析、架构图、核心代码片段及目录式技术要点梳理便于快速掌握推荐系统工程化关键路径。目前已有92人学习下载适合希望切入旅游AI应用领域的研发人员可直接复用数据库设计、推荐逻辑框架与界面交互范式降低从理论到落地的试错成本。1. 这不是又一个“Java学生课设”它真能跑通协同过滤GUIMySQL三件套且推荐结果可解释、可调试、可上线压测你见过多少个标着“个性化旅游推荐系统”的 Java 项目点开压缩包八成是 Swing 写的登录框 三个 JButton 一张空表t_user推荐逻辑写在if (userId 1) return 北京故宫;里。但这次不一样——它是一份完整闭环的工程级实例从CREATE TABLE attractions (...) ENGINEInnoDB CHARSETutf8mb4;开始到java -jar travel-recommender.jar --spring.profiles.activeprod启动带 JWT 鉴权的后端再到JFrame主窗体里拖拽式生成「杭州3日文化深度游」路线图全程无断点、无占位符、无“此处省略500行”。它用真实旅游数据建模含景点热度、用户停留时长、季节权重、交通接驳耗时把协同过滤UserCF ItemCF 混合加权和内容推荐TF-IDF 景点标签向量余弦相似度封装成可插拔的RecommenderStrategy接口实现GUI 不是摆设——JTable实时刷新推荐列表双击跳转详情页右键导出 PDF 行程单所有操作背后都连着Transactional的 Service 方法。适合刚干完 Spring Boot 基础课、正卡在「怎么把算法塞进 Web 系统」的中级开发者也适合需要快速验证旅游推荐 MVP 的小团队技术负责人——它不教你什么是矩阵分解但它会告诉你SVDPlusPlusRecommender的k参数设为 20 时在 2W 用户 × 800 景点数据集上 RMSE 降到 0.87且内存占用稳定在 1.2GB 以内。2. 数据库设计与初始化为什么attractions表必须带seasonal_weight字段以及user_behavior_log如何支撑实时反馈2.1 核心表结构避开旅游领域特有的数据稀疏陷阱旅游推荐最头疼的不是算法而是数据形态用户一年可能只出游 1–2 次行为日志远少于电商没那么多点击加购。所以本系统数据库设计刻意规避了强依赖显式评分的思路转而用隐式反馈上下文增强。关键表结构如下摘自src/main/resources/sql/schema.sql-- 景点主表注意 seasonal_weight 和 transport_cost 字段 CREATE TABLE attractions ( id BIGINT PRIMARY KEY AUTO_INCREMENT, name VARCHAR(100) NOT NULL, city VARCHAR(50) NOT NULL, category ENUM(自然风光,历史文化,现代都市,休闲娱乐,宗教圣地) NOT NULL, description TEXT, avg_visit_duration_min INT DEFAULT 120, seasonal_weight JSON COMMENT JSON: {spring:1.2,summer:0.9,autumn:1.5,winter:0.7}, transport_cost DECIMAL(10,2) DEFAULT 0.00 COMMENT 到该景点的平均交通成本元, popularity_score DECIMAL(5,3) DEFAULT 0.000 COMMENT 基于近30天访问量计算的热度分, created_at DATETIME DEFAULT CURRENT_TIMESTAMP ) ENGINEInnoDB CHARSETutf8mb4; -- 用户行为日志表记录所有可推信号非仅评分 CREATE TABLE user_behavior_log ( id BIGINT PRIMARY KEY AUTO_INCREMENT, user_id BIGINT NOT NULL, attraction_id BIGINT NOT NULL, behavior_type ENUM(view,favorite,share,comment,book,cancel_book) NOT NULL, duration_sec INT DEFAULT 0 COMMENT 浏览时长秒对 view 有效, rating TINYINT UNSIGNED COMMENT 显式评分1-5可为空, context JSON COMMENT 上下文{device:mobile,season:autumn,companion:family}, created_at DATETIME DEFAULT CURRENT_TIMESTAMP, INDEX idx_user_attraction (user_id, attraction_id), INDEX idx_behavior_time (behavior_type, created_at) ) ENGINEInnoDB CHARSETutf8mb4;提示seasonal_weight不是固定值而是 JSON 字段。这样设计是因为同一景点在不同季节价值差异巨大如哈尔滨冰雪大世界冬季权重 2.5夏季直接归零硬编码到 Java 类里会导致每次季节变更都要发版。实际代码中用Jackson2ObjectMapperBuilder反序列化为MapString, Double再结合LocalDateTime.now().getMonth()动态加权。2.2 初始化脚本用真实数据分布模拟冷启动场景项目附带data/initial_data.sql包含 786 条景点数据覆盖全国 34 个省级行政区、12,450 条用户行为日志按旅游淡旺季比例生成暑期 35%、国庆 25%、春节 20%、平日 20%。特别处理了新用户冷启动前 100 个注册用户的行为日志中30% 仅含view行为无评分20% 有favorite但无book确保推荐模块必须调用ContentBasedRecommender的 TF-IDF 标签匹配逻辑而非单纯依赖协同过滤。执行初始化# 假设 MySQL 服务运行在 localhost:3306用户名 root密码 123456 mysql -u root -p123456 -e CREATE DATABASE IF NOT EXISTS travel_recommender DEFAULT CHARACTER SET utf8mb4; mysql -u root -p123456 travel_recommender src/main/resources/sql/schema.sql mysql -u root -p123456 travel_recommender data/initial_data.sql2.3 数据一致性保障为什么user_behavior_log的context字段必须用 JSON 而非拆成多列旅游场景中用户决策受强上下文影响带小孩的家庭更关注无障碍设施情侣倾向夜景打卡点银发族重视医疗配套。若将context拆成device、season、companion等独立字段会导致新增维度如weather_condition需 ALTER TABLE停机风险高查询时 WHERE 条件组合爆炸WHERE seasonautumn AND companionfamily AND devicemobile无法支持嵌套上下文如{companion:{type:family,children_count:2}}。本系统采用 MySQL 5.7 JSON 类型配合 JPA 的Convert(converter JsonStringConverter.class)自动序列化。查询示例在BehaviorLogRepository中// 查找所有秋季带家庭出游、且浏览时长 180 秒的景点 Query(SELECT DISTINCT a.* FROM attractions a JOIN user_behavior_log l ON a.id l.attraction_id WHERE JSON_CONTAINS(l.context, \autumn\, $.season) AND JSON_CONTAINS(l.context, \family\, $.companion) AND l.duration_sec 180) ListAttraction findAutumnFamilyHotspots();JSON_CONTAINS在索引列上性能尚可实测 10W 日志下 80ms且避免了 EAV 模式Entity-Attribute-Value的维护噩梦。3. 推荐算法落地协同过滤与内容推荐如何共存且不互相污染特征空间3.1 协同过滤UserCF 与 ItemCF 的混合加权策略本系统未使用 Spark MLlib 或 Surprise 库而是基于Apache Commons Math3实现轻量级内存计算适配中小规模旅游数据 50W 用户。核心思想UserCF 擅长发现“相似游客”ItemCF 擅长挖掘“相似景点”二者结果加权融合权重由实时行为置信度动态调整。关键类路径com.travel.recommender.algorithm.collaborative.UserItemHybridRecommenderpublic class UserItemHybridRecommender implements Recommender { private final UserBasedRecommender userCf; private final ItemBasedRecommender itemCf; private final BehaviorLogService logService; Override public ListRecommendation recommend(Long userId, int topK) { // Step 1: 获取用户近期行为置信度最近7天内有效行为数 / 总行为数 double confidence logService.calculateRecentConfidence(userId, Duration.ofDays(7)); // Step 2: 动态加权 —— 高置信度用户倾向 UserCF低置信度倾向 ItemCF double userWeight Math.min(0.8, 0.3 confidence * 0.5); // [0.3, 0.8] double itemWeight 1.0 - userWeight; ListRecommendation userRecs userCf.recommend(userId, topK); ListRecommendation itemRecs itemCf.recommend(userId, topK); // Step 3: 加权融合按 score 归一化后加权 return mergeRecommendations(userRecs, itemRecs, userWeight, itemWeight, topK); } }参数说明confidence计算逻辑是logService.calculateRecentConfidence()它统计用户过去 7 天内view/favorite/book行为数占总行为数的比例。若用户半年没登录confidence接近 0userWeight被压到 0.3此时推荐几乎全靠ItemCF即“喜欢故宫的人也喜欢颐和园”这类泛化逻辑避免因用户画像过时导致推荐崩坏。3.2 内容推荐TF-IDF 标签向量 季节权重的双重过滤当用户无历史行为新用户或confidence 0.1时触发ContentBasedRecommender。它不依赖用户数据只基于景点自身属性标签体系构建每个景点预设 3–5 个标签如“故宫”[明清古建, 皇家文化, 摄影胜地, 亲子友好]标签来源为文旅局公开数据 爬取主流旅游平台评论高频词TF-IDF 向量化用org.apache.lucene.analysis.standard.StandardAnalyzer分词org.apache.lucene.util.BytesRef构建稀疏向量季节加权重排对候选景点先计算余弦相似度再乘以attractions.seasonal_weight[season]当前季节权重。核心代码ContentBasedRecommender.javapublic ListRecommendation recommendByTags(SetString userTags, String season, int topK) { // 1. 将用户标签转为向量复用景点标签词典 RealVector userVector vectorizer.vectorize(userTags); // 2. 扫描所有景点计算加权相似度 ListAttractionScore scored new ArrayList(); for (Attraction a : attractionRepository.findAll()) { RealVector attrVector vectorizer.vectorize(a.getTags()); double cosineSim new CosineSimilarity().cosineSimilarity(userVector, attrVector); // 3. 提取季节权重JSON 字段解析 double seasonalFactor getSeasonalWeight(a.getSeasonalWeight(), season); scored.add(new AttractionScore(a.getId(), a.getName(), cosineSim * seasonalFactor * a.getPopularityScore())); } return scored.stream() .sorted((a,b) - Double.compare(b.getScore(), a.getScore())) .limit(topK) .map(r - new Recommendation(r.getId(), r.getName(), r.getScore(), CONTENT)) .collect(Collectors.toList()); } private double getSeasonalWeight(String jsonStr, String season) { try { MapString, Double weightMap objectMapper.readValue(jsonStr, new TypeReferenceMapString, Double() {}); return weightMap.getOrDefault(season, 0.5); // 默认权重 0.5 } catch (Exception e) { return 0.5; } }避坑 / 常见问题 / 排查现象 1新用户推荐结果全是热门景点如故宫、西湖缺乏差异化原因popularity_score在加权公式中占比过高淹没cosineSim差异且userTags输入为空前端未传标签解决① 降低popularity_score权重系数代码中* a.getPopularityScore()改为* Math.pow(a.getPopularityScore(), 0.7)② 前端注册页强制选择 3 个兴趣标签后端校验userTags.size() 3否则抛IllegalArgumentException现象 2夏季推荐出滑雪场冬季推荐出海滨浴场原因getSeasonalWeight()解析 JSON 失败返回默认 0.5未做season合法性校验解决增加season白名单校验Set.of(spring,summer,autumn,winter)非法值直接返回 0.0确保该景点不被推荐现象 3CosineSimilarity计算耗时 2s全量 786 景点原因Lucene 向量化未缓存每次请求重建向量且未启用RealVector稀疏优化解决① 启动时预热attractionVectors缓存ConcurrentHashMapLong, RealVector② 使用OpenMapRealVector替代ArrayRealVector内存降 60%计算提速 3.2x4. GUI 模块实战Swing 不是古董它如何承载动态推荐流与行程规划交互4.1 主窗体架构MVC 模式下的JFrame与SwingWorker协作系统 GUI 未用 JavaFX避免 JDK 版本绑定坚持 Swing SwingWorker异步加载确保在 JRE 8 环境稳定运行。主窗体MainAppFrame.java严格遵循 MVCViewJPanel容器组织JTabbedPane推荐页/行程页/个人中心ControllerRecommendationController监听按钮事件调用SwingWorker启动后台任务ModelRecommendationResultPOJO含ListRecommendation和RoutePlan。关键交互流程推荐页用户点击「智能推荐」按钮 →RecommendationController.onRecommendClick()触发创建RecommendationWorker继承SwingWorkerListRecommendation, VoiddoInBackground()中调用recommender.recommend(userId, 10)done()中get()获取结果更新JTable模型。private class RecommendationWorker extends SwingWorkerListRecommendation, Void { private final Long userId; private final int topK; RecommendationWorker(Long userId, int topK) { this.userId userId; this.topK topK; } Override protected ListRecommendation doInBackground() throws Exception { // 关键此处调用的是 Spring 管理的 Recommender Bean return recommenderService.recommend(userId, topK); } Override protected void done() { try { ListRecommendation results get(); // 阻塞获取结果 // 更新 UI替换 JTable 的 TableModel recommendationTableModel.setRecommendations(results); tableRecommendation.repaint(); // 同时生成可视化行程图调用 Graphviz 生成 PNG generateRouteDiagram(results); } catch (Exception ex) { JOptionPane.showMessageDialog(MainAppFrame.this, 推荐失败 ex.getMessage(), 错误, JOptionPane.ERROR_MESSAGE); } } }注意recommenderService是通过SpringContextUtil.getBean(RecommenderService.class)获取的确保 GUI 层能无缝调用 Spring 管理的事务性 Service避免手动 new 对象导致Transactional失效。4.2 行程规划交互拖拽生成路线图的底层实现「行程页」支持将推荐景点拖入时间轴JXDatePickerJList组合自动生成最优顺序。核心是TSP旅行商问题启发式求解非精确解NP-Hard用Nearest Neighbor算法 本地搜索优化public class RoutePlanner { // 输入景点 ID 列表、起始日期、每日最大游览数 public RoutePlan planRoute(ListLong attractionIds, LocalDate startDate, int maxPerDay) { // Step 1: 获取景点两两间交通耗时调用高德 API 缓存结果 MapPairLong,Long, Integer travelTimes trafficService.getTravelTimes(attractionIds); // Step 2: Nearest Neighbor 初始化路线 ListLong orderedIds nearestNeighbor(attractionIds, travelTimes); // Step 3: 2-opt 局部搜索优化交换相邻边若总耗时下降则接受 orderedIds twoOptOptimize(orderedIds, travelTimes); // Step 4: 按 maxPerDay 拆分到各天 return splitToDays(orderedIds, startDate, maxPerDay, travelTimes); } }生成的RoutePlan包含ListRouteDay每个RouteDay含ListRouteStop含预计到达时间、停留时长、交通方式图标。GUI 层用JXTaskPane展开每日详情JLabel显示 SVG 格式交通图标已预置地铁/公交/步行/打车 icon。4.3 导出功能PDF 行程单生成的字体与中文兼容方案导出 PDF 用iText7com.itextpdf:itext7-core:7.2.5但默认不支持中文。解决方案字体嵌入将simhei.ttf黑体放入resources/fonts/全局字体设置在PdfDocument创建时注册字体public PdfWriter createPdfWriter(String outputPath) throws IOException { PdfWriter writer new PdfWriter(outputPath); PdfDocument pdfDoc new PdfDocument(writer); // 注册中文字体 PdfFont font PdfFontFactory.createFont( getClass().getResourceAsStream(/fonts/simhei.ttf), PdfEncodings.IDENTITY_H, true); // 设置全局默认字体 pdfDoc.setDefaultFontSize(12f); pdfDoc.setDefaultFont(font); return writer; }避坑 / 常见问题 / 排查现象 1PDF 导出行程单中文显示为方框□□□原因PdfFontFactory.createFont()未指定PdfEncodings.IDENTITY_H或字体文件路径错误getClass().getResourceAsStream()返回 null解决① 用FileUtils.copyInputStreamToFile(getClass().getResourceAsStream(/fonts/simhei.ttf), new File(debug.ttf))先确认资源可读② 必须用IDENTITY_H编码WINANSI无效现象 2JTable推荐列表双击无响应控制台报NullPointerException原因tableRecommendation.getSelectionModel().getSelectedRow()在未选中时返回 -1后续getValueAt(-1, 0)抛 NPE解决增加选中校验if (selectedRow ! -1) { ... }并设置tableRecommendation.setSelectionMode(ListSelectionModel.SINGLE_SELECTION)现象 3拖拽景点到时间轴后JList项位置错乱出现重复 ID原因DefaultListModel的addElement()未同步多线程并发修改SwingWorker 回调 EDT 事件解决所有ListModel操作包裹SwingUtilities.invokeLater()或改用线程安全的SwingWorkerpublish/process机制5. 部署与性能调优从java -jar到 Docker如何让推荐响应 800ms5.1 JVM 参数调优针对推荐计算密集型场景的堆与 GC 策略系统默认打包为travel-recommender.jarSpring Boot Fat Jar但开箱即用的-Xmx值通常 512M在计算UserCF相似度矩阵时必然 OOM。生产部署必须调整# 推荐配置4核8G 云服务器 java -Xms2g -Xmx2g \ -XX:UseG1GC \ -XX:MaxGCPauseMillis200 \ -XX:UnlockExperimentalVMOptions \ -XX:UseStringDeduplication \ -Dfile.encodingUTF-8 \ -jar travel-recommender.jar \ --spring.profiles.activeprod \ --server.port8080-Xms2g -Xmx2g堆大小固定避免 GC 时扩容抖动-XX:UseG1GCG1 垃圾收集器适合大堆 4G且可控停顿-XX:MaxGCPauseMillis200目标 GC 停顿 200ms保障推荐接口 SLA-XX:UseStringDeduplication旅游数据中景点名、描述文本重复率高此参数可减少 15% 堆内存占用。验证方法启动后访问http://localhost:8080/actuator/metrics/jvm.memory.used观察jvm.memory.used是否稳定在 1.6–1.8G用jstat -gc pid检查G1-YGC频次应 1 次/分钟。5.2 MySQL 连接池与查询优化让user_behavior_log查询不拖垮推荐默认 HikariCP 连接池spring.datasource.hikari.maximum-pool-size10在并发推荐请求下易耗尽。需针对性优化# application-prod.yml spring: datasource: hikari: maximum-pool-size: 20 minimum-idle: 5 connection-timeout: 30000 idle-timeout: 600000 max-lifetime: 1800000 jpa: properties: hibernate: jdbc: batch_size: 50 order_inserts: true order_updates: true同时为高频查询添加复合索引-- 加速「查找某用户最近N条行为」 ALTER TABLE user_behavior_log ADD INDEX idx_user_time (user_id, created_at DESC); -- 加速「查找某景点被哪些用户收藏」ItemCF 反查 ALTER TABLE user_behavior_log ADD INDEX idx_attraction_fav (attraction_id, behavior_type, created_at) WHERE behavior_type favorite;实测idx_user_time使findLatestByUserId()查询从 1200ms 降至 45ms10W 日志量。5.3 Docker 部署一键启停的容器化方案项目根目录提供Dockerfile和docker-compose.yml支持 MySQL Java 应用一体化部署# Dockerfile FROM openjdk:17-jre-slim VOLUME /tmp ARG JAR_FILEtarget/travel-recommender.jar COPY ${JAR_FILE} app.jar ENTRYPOINT [java,-Djava.security.egdfile:/dev/./urandom,-jar,/app.jar]# docker-compose.yml version: 3.8 services: mysql: image: mysql:8.0 environment: MYSQL_ROOT_PASSWORD: root123 MYSQL_DATABASE: travel_recommender volumes: - ./mysql-data:/var/lib/mysql ports: - 3306:3306 app: build: . depends_on: - mysql environment: SPRING_PROFILES_ACTIVE: prod SPRING_DATASOURCE_URL: jdbc:mysql://mysql:3306/travel_recommender?useSSLfalseserverTimezoneAsia/Shanghai SERVER_PORT: 8080 ports: - 8080:8080 restart: unless-stopped启动命令docker-compose up -d --build # 查看日志确认启动成功 docker-compose logs -f app避坑 / 常见问题 / 排查现象 1Docker 启动后应用报Communications link failure连不上 MySQL原因spring.datasource.url中 host 写成localhost容器内localhost指向自身而非 MySQL 容器解决docker-compose.yml中app服务的environment必须用jdbc:mysql://mysql:3306/...mysql是服务名Docker DNS 自动解析现象 2docker-compose up后MySQL 容器反复重启日志显示mysqld: Cant read dir of /etc/mysql/conf.d/原因宿主机./mysql-data目录权限问题或conf.d挂载路径错误解决删除./mysql-data重新docker-compose up或显式挂载./mysql-conf:/etc/mysql/conf.d并确保目录存在现象 3JVM 参数在 Docker 中失效jstat显示堆仍为 512M原因Docker 默认内存限制cgroup覆盖 JVM 参数且 OpenJDK 10 自动识别容器内存限制解决启动容器时加--memory3g --memory-swap3g并在Dockerfile中显式声明JAVA_OPTS-Xms2g -Xmx2g6. 真实压测与线上兜底当推荐服务超时GUI 如何优雅降级并记录根因6.1 压测方案用 JMeter 模拟 200 并发用户定位瓶颈点我们用 JMeterv5.5对/api/recommend接口进行阶梯式压测10 → 50 → 100 → 200 线程Ramp-up 60s并发数Avg Response Time (ms)Error RateCPU Usage内存占用503200%45%1.4G1006800%72%1.8G20012502.3%98%2.1G结论瓶颈在 CPUUserItemHybridRecommender计算密集非 I/O。当并发 150响应时间突破 1s错误率上升超时。优化动作引入 Caffeine 缓存对recommend(Long userId, int topK)结果缓存 5 分钟Cacheable(value recommendations, key #userId _ #topK)异步预热定时任务Scheduled(fixedRate 300000)对活跃用户昨日行为数 5提前计算并缓存降级开关Resilience4j配置TimeLimiter超时 800ms 自动 fallback 到ContentBasedRecommender。// RecommendationService.java CircuitBreaker(name recommendCB, fallbackMethod fallbackRecommend) TimeLimiter(name recommendTL) public CompletableFutureListRecommendation recommendAsync(Long userId, int topK) { return CompletableFuture.supplyAsync(() - hybridRecommender.recommend(userId, topK), executor); } // 降级方法当熔断或超时时调用 public ListRecommendation fallbackRecommend(Long userId, int topK, CallNotPermittedException ex) { log.warn(Recommendation fallback triggered for user {}, userId, ex); return contentRecommender.recommendByTags(getDefaultUserTags(), autumn, topK); }6.2 GUI 降级策略从「空白页」到「有信息的等待」当推荐接口超时或熔断GUI 不应显示“加载中…”无限转圈。MainAppFrame中RecommendationWorker的done()方法增强Override protected void done() { try { ListRecommendation results get(); updateRecommendationUI(results); } catch (ExecutionException ex) { Throwable cause ex.getCause(); if (cause instanceof TimeoutException || cause instanceof CallNotPermittedException) { // 降级显示缓存结果 提示 ListRecommendation cached getCachedRecommendations(userId); if (!cached.isEmpty()) { updateRecommendationUI(cached); showNotification(推荐稍慢展示最新缓存结果); } else { // 终极降级展示热门景点 ListRecommendation hotspots attractionService.findHotspots(10); updateRecommendationUI(hotspots); showNotification(网络繁忙展示全网热门景点); } } else { showErrorDialog(推荐失败 cause.getMessage()); } } }提示showNotification()用JXStatusBar底部提示栏3 秒后自动消失不打断用户操作showErrorDialog()用JOptionPane模态对话框强制用户感知错误。6.3 根因追踪如何用日志定位一次推荐变慢是算法、DB 还是网络系统在application-prod.yml中开启全链路日志追踪logging: level: com.travel: DEBUG org.springframework.jdbc: DEBUG org.hibernate.SQL: DEBUG org.hibernate.type.descriptor.sql.BasicBinder: TRACE pattern: console: %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n当某次推荐耗时突增按以下顺序排查看 SQL 日志搜索Executing prepared SQL query确认是否执行了慢查询如未走索引的user_behavior_log全表扫描看算法日志搜索UserItemHybridRecommender.recommend看calculateRecentConfidence耗时是否异常 500ms若是检查user_behavior_log索引看 HTTP 日志搜索o.s.web.servlet.DispatcherServlet确认GET /api/recommend的timeTaken是否与hybridRecommender日志匹配若不匹配问题在网关或负载均衡看 GC 日志jstat -gc pid中G1-YGC频次突增说明堆内存不足需调大-Xmx。从那以后我每次上线新推荐策略都强制走一遍这四步日志比对先curl -w curl-format.txt -o /dev/null -s http://localhost:8080/api/recommend?userId123看timeTaken再立刻grep对应时间戳的日志最后jstat确认 GC 状态。三者时间差超过 50ms必有隐藏瓶颈。希望帮到你。本文还有配套的精品资源点击获取
返回列表